Skip to content

Commit

Permalink
usb-wwan: implement TIOCGSERIAL and TIOCSSERIAL to avoid blocking clo…
Browse files Browse the repository at this point in the history
…se(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>
  • Loading branch information
Dan Williams authored and Greg Kroah-Hartman committed Dec 1, 2010
1 parent 73f35c6 commit 02303f7
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/usb/serial/option.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/serial/usb-wwan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
78 changes: 78 additions & 0 deletions drivers/usb/serial/usb_wwan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 02303f7

Please sign in to comment.