mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 09:50:04 +00:00
cdc-acm: add TIOCMIWAIT
This implements TIOCMIWAIT for TIOCM_DSR, TIOCM_RI and TIOCM_CD Disconnect is handled as TIOCM_CD or an error. Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
4e065b8bba
commit
5a6a62bdb9
2 changed files with 77 additions and 12 deletions
|
@ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb)
|
|||
struct usb_cdc_notification *dr = urb->transfer_buffer;
|
||||
unsigned char *data;
|
||||
int newctrl;
|
||||
int difference;
|
||||
int retval;
|
||||
int status = urb->status;
|
||||
|
||||
|
@ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb)
|
|||
tty_port_tty_hangup(&acm->port, false);
|
||||
}
|
||||
|
||||
difference = acm->ctrlin ^ newctrl;
|
||||
spin_lock(&acm->read_lock);
|
||||
acm->ctrlin = newctrl;
|
||||
acm->oldcount = acm->iocount;
|
||||
|
||||
dev_dbg(&acm->control->dev,
|
||||
"%s - input control lines: dcd%c dsr%c break%c "
|
||||
"ring%c framing%c parity%c overrun%c\n",
|
||||
__func__,
|
||||
acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
|
||||
acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
|
||||
break;
|
||||
if (difference & ACM_CTRL_DSR)
|
||||
acm->iocount.dsr++;
|
||||
if (difference & ACM_CTRL_BRK)
|
||||
acm->iocount.brk++;
|
||||
if (difference & ACM_CTRL_RI)
|
||||
acm->iocount.rng++;
|
||||
if (difference & ACM_CTRL_DCD)
|
||||
acm->iocount.dcd++;
|
||||
if (difference & ACM_CTRL_FRAMING)
|
||||
acm->iocount.frame++;
|
||||
if (difference & ACM_CTRL_PARITY)
|
||||
acm->iocount.parity++;
|
||||
if (difference & ACM_CTRL_OVERRUN)
|
||||
acm->iocount.overrun++;
|
||||
spin_unlock(&acm->read_lock);
|
||||
|
||||
if (difference)
|
||||
wake_up_all(&acm->wioctl);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(&acm->control->dev,
|
||||
|
@ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int wait_serial_change(struct acm *acm, unsigned long arg)
|
||||
{
|
||||
int rv = 0;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
struct async_icount old, new;
|
||||
|
||||
if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD ))
|
||||
return -EINVAL;
|
||||
do {
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
old = acm->oldcount;
|
||||
new = acm->iocount;
|
||||
acm->oldcount = new;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
|
||||
if ((arg & TIOCM_DSR) &&
|
||||
old.dsr != new.dsr)
|
||||
break;
|
||||
if ((arg & TIOCM_CD) &&
|
||||
old.dcd != new.dcd)
|
||||
break;
|
||||
if ((arg & TIOCM_RI) &&
|
||||
old.rng != new.rng)
|
||||
break;
|
||||
|
||||
add_wait_queue(&acm->wioctl, &wait);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
remove_wait_queue(&acm->wioctl, &wait);
|
||||
if (acm->disconnected) {
|
||||
if (arg & TIOCM_CD)
|
||||
break;
|
||||
else
|
||||
rv = -ENODEV;
|
||||
} else {
|
||||
if (signal_pending(current))
|
||||
rv = -ERESTARTSYS;
|
||||
}
|
||||
} while (!rv);
|
||||
|
||||
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int acm_tty_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
|
@ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty,
|
|||
case TIOCSSERIAL:
|
||||
rv = set_serial_info(acm, (struct serial_struct __user *) arg);
|
||||
break;
|
||||
case TIOCMIWAIT:
|
||||
rv = wait_serial_change(acm, arg);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -1167,6 +1227,7 @@ static int acm_probe(struct usb_interface *intf,
|
|||
acm->readsize = readsize;
|
||||
acm->rx_buflimit = num_rx_buf;
|
||||
INIT_WORK(&acm->work, acm_softint);
|
||||
init_waitqueue_head(&acm->wioctl);
|
||||
spin_lock_init(&acm->write_lock);
|
||||
spin_lock_init(&acm->read_lock);
|
||||
mutex_init(&acm->mutex);
|
||||
|
@ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf)
|
|||
device_remove_file(&acm->control->dev,
|
||||
&dev_attr_iCountryCodeRelDate);
|
||||
}
|
||||
wake_up_all(&acm->wioctl);
|
||||
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
|
||||
usb_set_intfdata(acm->control, NULL);
|
||||
usb_set_intfdata(acm->data, NULL);
|
||||
|
|
|
@ -106,6 +106,9 @@ struct acm {
|
|||
struct work_struct work; /* work queue entry for line discipline waking up */
|
||||
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
|
||||
unsigned int ctrlout; /* output control lines (DTR, RTS) */
|
||||
struct async_icount iocount; /* counters for control line changes */
|
||||
struct async_icount oldcount; /* for comparison of counter */
|
||||
wait_queue_head_t wioctl; /* for ioctl */
|
||||
unsigned int writesize; /* max packet size for the output bulk endpoint */
|
||||
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
|
||||
unsigned int minor; /* acm minor number */
|
||||
|
|
Loading…
Reference in a new issue