136 lines
4.5 KiB
Diff
136 lines
4.5 KiB
Diff
commit 02303f73373aa1da19dbec510ec5a4e2576f9610
|
|
Author: Dan Williams <dcbw@redhat.com>
|
|
Date: Fri Nov 19 16:04:00 2010 -0600
|
|
|
|
usb-wwan: implement TIOCGSERIAL and TIOCSSERIAL to avoid blocking close(2)
|
|
|
|
Some devices (ex ZTE 2726) simply don't respond at all when data is sent
|
|
to some of their USB interfaces. The data gets stuck in the TTYs queue
|
|
and sits there until close(2), which them blocks because closing_wait
|
|
defaults to 30 seconds (even though the fd is O_NONBLOCK). This is
|
|
rarely desired. Implement the standard mechanism to adjust closing_wait
|
|
and let applications handle it how they want to.
|
|
|
|
Signed-off-by: Dan Williams <dcbw@redhat.com>
|
|
|
|
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
|
|
index 2297fb1..2a56cc3 100644
|
|
--- a/drivers/usb/serial/option.c
|
|
+++ b/drivers/usb/serial/option.c
|
|
@@ -989,6 +989,7 @@ static struct usb_serial_driver option_1port_device = {
|
|
.set_termios = usb_wwan_set_termios,
|
|
.tiocmget = usb_wwan_tiocmget,
|
|
.tiocmset = usb_wwan_tiocmset,
|
|
+ .ioctl = usb_wwan_ioctl,
|
|
.attach = usb_wwan_startup,
|
|
.disconnect = usb_wwan_disconnect,
|
|
.release = usb_wwan_release,
|
|
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
|
|
index 2be298a..3ab77c5 100644
|
|
--- a/drivers/usb/serial/usb-wwan.h
|
|
+++ b/drivers/usb/serial/usb-wwan.h
|
|
@@ -18,6 +18,8 @@ extern void usb_wwan_set_termios(struct tty_struct *tty,
|
|
extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file);
|
|
extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
|
|
unsigned int set, unsigned int clear);
|
|
+extern int usb_wwan_ioctl(struct tty_struct *tty, struct file *file,
|
|
+ unsigned int cmd, unsigned long arg);
|
|
extern int usb_wwan_send_setup(struct usb_serial_port *port);
|
|
extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|
const unsigned char *buf, int count);
|
|
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
|
|
index fbc9467..660b7ca 100644
|
|
--- a/drivers/usb/serial/usb_wwan.c
|
|
+++ b/drivers/usb/serial/usb_wwan.c
|
|
@@ -33,6 +33,7 @@
|
|
#include <linux/bitops.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/serial.h>
|
|
+#include <linux/serial.h>
|
|
#include "usb-wwan.h"
|
|
|
|
static int debug;
|
|
@@ -123,6 +124,83 @@ int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
|
|
}
|
|
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, struct file *file,
|
|
+ unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ struct usb_serial_port *port = tty->driver_data;
|
|
+
|
|
+ dbg("%s cmd 0x%04x", __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;
|
|
+ }
|
|
+
|
|
+ dbg("%s arg not supported", __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)
|