|
@@ -58,7 +58,6 @@
|
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
|
#include <linux/ethtool.h>
|
|
#include <linux/ethtool.h>
|
|
|
#include <linux/usb.h>
|
|
#include <linux/usb.h>
|
|
|
-#include <linux/timer.h>
|
|
|
|
|
#include <linux/tty.h>
|
|
#include <linux/tty.h>
|
|
|
#include <linux/tty_driver.h>
|
|
#include <linux/tty_driver.h>
|
|
|
#include <linux/tty_flip.h>
|
|
#include <linux/tty_flip.h>
|
|
@@ -154,6 +153,7 @@ struct hso_net {
|
|
|
struct hso_device *parent;
|
|
struct hso_device *parent;
|
|
|
struct net_device *net;
|
|
struct net_device *net;
|
|
|
struct rfkill *rfkill;
|
|
struct rfkill *rfkill;
|
|
|
|
|
+ char name[24];
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor *in_endp;
|
|
struct usb_endpoint_descriptor *in_endp;
|
|
|
struct usb_endpoint_descriptor *out_endp;
|
|
struct usb_endpoint_descriptor *out_endp;
|
|
@@ -274,7 +274,6 @@ struct hso_device {
|
|
|
u8 usb_gone;
|
|
u8 usb_gone;
|
|
|
struct work_struct async_get_intf;
|
|
struct work_struct async_get_intf;
|
|
|
struct work_struct async_put_intf;
|
|
struct work_struct async_put_intf;
|
|
|
- struct work_struct reset_device;
|
|
|
|
|
|
|
|
|
|
struct usb_device *usb;
|
|
struct usb_device *usb;
|
|
|
struct usb_interface *interface;
|
|
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_put_activity(struct hso_device *hso_dev);
|
|
|
static int hso_get_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 tiocmget_intr_callback(struct urb *urb);
|
|
|
-static void reset_device(struct work_struct *data);
|
|
|
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
|
/* Helping functions */
|
|
/* Helping functions */
|
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
@@ -696,7 +694,7 @@ static void handle_usb_error(int status, const char *function,
|
|
|
case -ETIMEDOUT:
|
|
case -ETIMEDOUT:
|
|
|
explanation = "protocol error";
|
|
explanation = "protocol error";
|
|
|
if (hso_dev)
|
|
if (hso_dev)
|
|
|
- schedule_work(&hso_dev->reset_device);
|
|
|
|
|
|
|
+ usb_queue_reset_device(hso_dev->interface);
|
|
|
break;
|
|
break;
|
|
|
default:
|
|
default:
|
|
|
explanation = "unknown status";
|
|
explanation = "unknown status";
|
|
@@ -1271,7 +1269,6 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
|
|
|
goto err_out;
|
|
goto err_out;
|
|
|
|
|
|
|
|
D1("Opening %d", serial->minor);
|
|
D1("Opening %d", serial->minor);
|
|
|
- kref_get(&serial->parent->ref);
|
|
|
|
|
|
|
|
|
|
/* setup */
|
|
/* setup */
|
|
|
tty->driver_data = serial;
|
|
tty->driver_data = serial;
|
|
@@ -1290,7 +1287,8 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
|
|
|
if (result) {
|
|
if (result) {
|
|
|
hso_stop_serial_device(serial->parent);
|
|
hso_stop_serial_device(serial->parent);
|
|
|
serial->port.count--;
|
|
serial->port.count--;
|
|
|
- kref_put(&serial->parent->ref, hso_serial_ref_free);
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ kref_get(&serial->parent->ref);
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
D1("Port was already open");
|
|
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);
|
|
usb_autopm_put_interface(serial->parent->interface);
|
|
|
|
|
|
|
|
mutex_unlock(&serial->parent->mutex);
|
|
mutex_unlock(&serial->parent->mutex);
|
|
|
-
|
|
|
|
|
- kref_put(&serial->parent->ref, hso_serial_ref_free);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* close the requested serial port */
|
|
/* close the requested serial port */
|
|
@@ -1392,6 +1388,16 @@ static int hso_serial_write_room(struct tty_struct *tty)
|
|
|
return room;
|
|
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 */
|
|
/* setup the term */
|
|
|
static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
|
|
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++) {
|
|
for (i = 0; i < serial->num_rx_urbs; i++) {
|
|
|
if (serial->rx_urb[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;
|
|
serial->curr_rx_urb_idx = 0;
|
|
@@ -2228,14 +2234,17 @@ static int hso_stop_serial_device(struct hso_device *hso_dev)
|
|
|
return 0;
|
|
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)
|
|
if (serial->parent->dev)
|
|
|
device_remove_file(serial->parent->dev, &dev_attr_hsotype);
|
|
device_remove_file(serial->parent->dev, &dev_attr_hsotype);
|
|
|
|
|
|
|
|
tty_unregister_device(tty_drv, serial->minor);
|
|
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++) {
|
|
for (i = 0; i < serial->num_rx_urbs; i++) {
|
|
|
/* unlink and free RX URB */
|
|
/* unlink and free RX URB */
|
|
@@ -2246,6 +2255,7 @@ static void hso_serial_common_free(struct hso_serial *serial)
|
|
|
|
|
|
|
|
/* unlink and free TX URB */
|
|
/* unlink and free TX URB */
|
|
|
usb_free_urb(serial->tx_urb);
|
|
usb_free_urb(serial->tx_urb);
|
|
|
|
|
+ kfree(serial->tx_buffer);
|
|
|
kfree(serial->tx_data);
|
|
kfree(serial->tx_data);
|
|
|
tty_port_destroy(&serial->port);
|
|
tty_port_destroy(&serial->port);
|
|
|
}
|
|
}
|
|
@@ -2316,6 +2326,7 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
exit:
|
|
exit:
|
|
|
|
|
+ hso_serial_tty_unregister(serial);
|
|
|
hso_serial_common_free(serial);
|
|
hso_serial_common_free(serial);
|
|
|
return -1;
|
|
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_get_intf, async_get_intf);
|
|
|
INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
|
|
INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
|
|
|
- INIT_WORK(&hso_dev->reset_device, reset_device);
|
|
|
|
|
|
|
|
|
|
return hso_dev;
|
|
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 hso_net *hso_net = dev2net(hso_dev);
|
|
|
struct device *dev = &hso_net->net->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,
|
|
&interface_to_usbdev(interface)->dev,
|
|
|
RFKILL_TYPE_WWAN,
|
|
RFKILL_TYPE_WWAN,
|
|
|
&hso_rfkill_ops, hso_dev);
|
|
&hso_rfkill_ops, hso_dev);
|
|
|
if (!hso_net->rfkill) {
|
|
if (!hso_net->rfkill) {
|
|
|
dev_err(dev, "%s - Out of memory\n", __func__);
|
|
dev_err(dev, "%s - Out of memory\n", __func__);
|
|
|
- kfree(rfkn);
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (rfkill_register(hso_net->rfkill) < 0) {
|
|
if (rfkill_register(hso_net->rfkill) < 0) {
|
|
|
rfkill_destroy(hso_net->rfkill);
|
|
rfkill_destroy(hso_net->rfkill);
|
|
|
- kfree(rfkn);
|
|
|
|
|
hso_net->rfkill = NULL;
|
|
hso_net->rfkill = NULL;
|
|
|
dev_err(dev, "%s - Failed to register rfkill\n", __func__);
|
|
dev_err(dev, "%s - Failed to register rfkill\n", __func__);
|
|
|
return;
|
|
return;
|
|
@@ -2594,7 +2598,6 @@ static void hso_free_serial_device(struct hso_device *hso_dev)
|
|
|
|
|
|
|
|
if (!serial)
|
|
if (!serial)
|
|
|
return;
|
|
return;
|
|
|
- set_serial_by_index(serial->minor, NULL);
|
|
|
|
|
|
|
|
|
|
hso_serial_common_free(serial);
|
|
hso_serial_common_free(serial);
|
|
|
|
|
|
|
@@ -2684,6 +2687,7 @@ static struct hso_device *hso_create_bulk_serial_device(
|
|
|
return hso_dev;
|
|
return hso_dev;
|
|
|
|
|
|
|
|
exit2:
|
|
exit2:
|
|
|
|
|
+ hso_serial_tty_unregister(serial);
|
|
|
hso_serial_common_free(serial);
|
|
hso_serial_common_free(serial);
|
|
|
exit:
|
|
exit:
|
|
|
hso_free_tiomget(serial);
|
|
hso_free_tiomget(serial);
|
|
@@ -3083,26 +3087,6 @@ static int hso_resume(struct usb_interface *iface)
|
|
|
return result;
|
|
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)
|
|
static void hso_serial_ref_free(struct kref *ref)
|
|
|
{
|
|
{
|
|
|
struct hso_device *hso_dev = container_of(ref, struct hso_device, 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)
|
|
static void hso_free_interface(struct usb_interface *interface)
|
|
|
{
|
|
{
|
|
|
- struct hso_serial *hso_dev;
|
|
|
|
|
|
|
+ struct hso_serial *serial;
|
|
|
int i;
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
|
|
for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
|
|
|
if (serial_table[i] &&
|
|
if (serial_table[i] &&
|
|
|
(serial_table[i]->interface == interface)) {
|
|
(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);
|
|
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,
|
|
.close = hso_serial_close,
|
|
|
.write = hso_serial_write,
|
|
.write = hso_serial_write,
|
|
|
.write_room = hso_serial_write_room,
|
|
.write_room = hso_serial_write_room,
|
|
|
|
|
+ .cleanup = hso_serial_cleanup,
|
|
|
.ioctl = hso_serial_ioctl,
|
|
.ioctl = hso_serial_ioctl,
|
|
|
.set_termios = hso_serial_set_termios,
|
|
.set_termios = hso_serial_set_termios,
|
|
|
.chars_in_buffer = hso_serial_chars_in_buffer,
|
|
.chars_in_buffer = hso_serial_chars_in_buffer,
|