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
@ -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)
|
|||||||
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;
|
||||||
|
|
||||||
dev_dbg(&acm->control->dev,
|
if (difference & ACM_CTRL_DSR)
|
||||||
"%s - input control lines: dcd%c dsr%c break%c "
|
acm->iocount.dsr++;
|
||||||
"ring%c framing%c parity%c overrun%c\n",
|
if (difference & ACM_CTRL_BRK)
|
||||||
__func__,
|
acm->iocount.brk++;
|
||||||
acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
|
if (difference & ACM_CTRL_RI)
|
||||||
acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
|
acm->iocount.rng++;
|
||||||
acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
|
if (difference & ACM_CTRL_DCD)
|
||||||
acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
|
acm->iocount.dcd++;
|
||||||
acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
|
if (difference & ACM_CTRL_FRAMING)
|
||||||
acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
|
acm->iocount.frame++;
|
||||||
acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
|
if (difference & ACM_CTRL_PARITY)
|
||||||
break;
|
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:
|
default:
|
||||||
dev_dbg(&acm->control->dev,
|
dev_dbg(&acm->control->dev,
|
||||||
@ -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,
|
|||||||
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 @@ made_compressed_probe:
|
|||||||
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)
|
|||||||
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 {
|
|||||||
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 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user