Skip to content

Commit

Permalink
cdc-acm: implement TIOCSSERIAL to avoid blocking close(2)
Browse files Browse the repository at this point in the history
Some devices (ex Nokia C7) 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.

See also 02303f7 for usb_wwan.c.

Signed-off-by: Dan Williams <dcbw@redhat.com>
Cc: stable <stable@vger.kernel.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Tested-by: Aleksander Morgado <aleksander@gnu.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Dan Williams authored and Greg Kroah-Hartman committed Nov 16, 2012
1 parent 73050d7 commit ba2d8ce
Showing 1 changed file with 38 additions and 0 deletions.
38 changes: 38 additions & 0 deletions drivers/usb/class/cdc-acm.c
Original file line number Diff line number Diff line change
Expand Up @@ -787,13 +787,48 @@ static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
tmp.flags = ASYNC_LOW_LATENCY;
tmp.xmit_fifo_size = acm->writesize;
tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
tmp.close_delay = acm->port.close_delay / 10;
tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
acm->port.closing_wait / 10;

if (copy_to_user(info, &tmp, sizeof(tmp)))
return -EFAULT;
else
return 0;
}

static int set_serial_info(struct acm *acm,
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(&acm->port.mutex);

if (!capable(CAP_SYS_ADMIN)) {
if ((close_delay != acm->port.close_delay) ||
(closing_wait != acm->port.closing_wait))
retval = -EPERM;
else
retval = -EOPNOTSUPP;
} else {
acm->port.close_delay = close_delay;
acm->port.closing_wait = closing_wait;
}

mutex_unlock(&acm->port.mutex);
return retval;
}

static int acm_tty_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
Expand All @@ -804,6 +839,9 @@ static int acm_tty_ioctl(struct tty_struct *tty,
case TIOCGSERIAL: /* gets serial port data */
rv = get_serial_info(acm, (struct serial_struct __user *) arg);
break;
case TIOCSSERIAL:
rv = set_serial_info(acm, (struct serial_struct __user *) arg);
break;
}

return rv;
Expand Down

0 comments on commit ba2d8ce

Please sign in to comment.