Commit 5a6a62bd authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman

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: default avatarOliver Neukum <oneukum@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4e065b8b
...@@ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb) ...@@ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb)
struct usb_cdc_notification *dr = urb->transfer_buffer; struct usb_cdc_notification *dr = urb->transfer_buffer;
unsigned char *data; unsigned char *data;
int newctrl; int newctrl;
int difference;
int retval; int retval;
int status = urb->status; int status = urb->status;
...@@ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb) ...@@ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb)
tty_port_tty_hangup(&acm->port, false); tty_port_tty_hangup(&acm->port, false);
} }
difference = acm->ctrlin ^ newctrl;
spin_lock(&acm->read_lock);
acm->ctrlin = newctrl; acm->ctrlin = newctrl;
acm->oldcount = acm->iocount;
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);
dev_dbg(&acm->control->dev, break;
"%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;
default: default:
dev_dbg(&acm->control->dev, dev_dbg(&acm->control->dev,
...@@ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm, ...@@ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm,
return retval; 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, static int acm_tty_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
...@@ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty, ...@@ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty,
case TIOCSSERIAL: case TIOCSSERIAL:
rv = set_serial_info(acm, (struct serial_struct __user *) arg); rv = set_serial_info(acm, (struct serial_struct __user *) arg);
break; break;
case TIOCMIWAIT:
rv = wait_serial_change(acm, arg);
break;
} }
return rv; return rv;
...@@ -1167,6 +1227,7 @@ static int acm_probe(struct usb_interface *intf, ...@@ -1167,6 +1227,7 @@ static int acm_probe(struct usb_interface *intf,
acm->readsize = readsize; acm->readsize = readsize;
acm->rx_buflimit = num_rx_buf; acm->rx_buflimit = num_rx_buf;
INIT_WORK(&acm->work, acm_softint); INIT_WORK(&acm->work, acm_softint);
init_waitqueue_head(&acm->wioctl);
spin_lock_init(&acm->write_lock); spin_lock_init(&acm->write_lock);
spin_lock_init(&acm->read_lock); spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex); mutex_init(&acm->mutex);
...@@ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf) ...@@ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf)
device_remove_file(&acm->control->dev, device_remove_file(&acm->control->dev,
&dev_attr_iCountryCodeRelDate); &dev_attr_iCountryCodeRelDate);
} }
wake_up_all(&acm->wioctl);
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->control, NULL);
usb_set_intfdata(acm->data, NULL); usb_set_intfdata(acm->data, NULL);
......
...@@ -106,6 +106,9 @@ struct acm { ...@@ -106,6 +106,9 @@ struct acm {
struct work_struct work; /* work queue entry for line discipline waking up */ 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 ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */ 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 writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int minor; /* acm minor number */ unsigned int minor; /* acm minor number */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment