Skip to content

Commit 5a6a62b

Browse files
Oliver Neukumgregkh
authored andcommitted
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 <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 4e065b8 commit 5a6a62b

File tree

2 files changed

+77
-12
lines changed

2 files changed

+77
-12
lines changed

drivers/usb/class/cdc-acm.c

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb)
262262
struct usb_cdc_notification *dr = urb->transfer_buffer;
263263
unsigned char *data;
264264
int newctrl;
265+
int difference;
265266
int retval;
266267
int status = urb->status;
267268

@@ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb)
302303
tty_port_tty_hangup(&acm->port, false);
303304
}
304305

306+
difference = acm->ctrlin ^ newctrl;
307+
spin_lock(&acm->read_lock);
305308
acm->ctrlin = newctrl;
309+
acm->oldcount = acm->iocount;
310+
311+
if (difference & ACM_CTRL_DSR)
312+
acm->iocount.dsr++;
313+
if (difference & ACM_CTRL_BRK)
314+
acm->iocount.brk++;
315+
if (difference & ACM_CTRL_RI)
316+
acm->iocount.rng++;
317+
if (difference & ACM_CTRL_DCD)
318+
acm->iocount.dcd++;
319+
if (difference & ACM_CTRL_FRAMING)
320+
acm->iocount.frame++;
321+
if (difference & ACM_CTRL_PARITY)
322+
acm->iocount.parity++;
323+
if (difference & ACM_CTRL_OVERRUN)
324+
acm->iocount.overrun++;
325+
spin_unlock(&acm->read_lock);
326+
327+
if (difference)
328+
wake_up_all(&acm->wioctl);
306329

307-
dev_dbg(&acm->control->dev,
308-
"%s - input control lines: dcd%c dsr%c break%c "
309-
"ring%c framing%c parity%c overrun%c\n",
310-
__func__,
311-
acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
312-
acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
313-
acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
314-
acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
315-
acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
316-
acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
317-
acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
318-
break;
330+
break;
319331

320332
default:
321333
dev_dbg(&acm->control->dev,
@@ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm,
796808
return retval;
797809
}
798810

811+
static int wait_serial_change(struct acm *acm, unsigned long arg)
812+
{
813+
int rv = 0;
814+
DECLARE_WAITQUEUE(wait, current);
815+
struct async_icount old, new;
816+
817+
if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD ))
818+
return -EINVAL;
819+
do {
820+
spin_lock_irq(&acm->read_lock);
821+
old = acm->oldcount;
822+
new = acm->iocount;
823+
acm->oldcount = new;
824+
spin_unlock_irq(&acm->read_lock);
825+
826+
if ((arg & TIOCM_DSR) &&
827+
old.dsr != new.dsr)
828+
break;
829+
if ((arg & TIOCM_CD) &&
830+
old.dcd != new.dcd)
831+
break;
832+
if ((arg & TIOCM_RI) &&
833+
old.rng != new.rng)
834+
break;
835+
836+
add_wait_queue(&acm->wioctl, &wait);
837+
set_current_state(TASK_INTERRUPTIBLE);
838+
schedule();
839+
remove_wait_queue(&acm->wioctl, &wait);
840+
if (acm->disconnected) {
841+
if (arg & TIOCM_CD)
842+
break;
843+
else
844+
rv = -ENODEV;
845+
} else {
846+
if (signal_pending(current))
847+
rv = -ERESTARTSYS;
848+
}
849+
} while (!rv);
850+
851+
852+
853+
return rv;
854+
}
855+
799856
static int acm_tty_ioctl(struct tty_struct *tty,
800857
unsigned int cmd, unsigned long arg)
801858
{
@@ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty,
809866
case TIOCSSERIAL:
810867
rv = set_serial_info(acm, (struct serial_struct __user *) arg);
811868
break;
869+
case TIOCMIWAIT:
870+
rv = wait_serial_change(acm, arg);
871+
break;
812872
}
813873

814874
return rv;
@@ -1167,6 +1227,7 @@ static int acm_probe(struct usb_interface *intf,
11671227
acm->readsize = readsize;
11681228
acm->rx_buflimit = num_rx_buf;
11691229
INIT_WORK(&acm->work, acm_softint);
1230+
init_waitqueue_head(&acm->wioctl);
11701231
spin_lock_init(&acm->write_lock);
11711232
spin_lock_init(&acm->read_lock);
11721233
mutex_init(&acm->mutex);
@@ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf)
13831444
device_remove_file(&acm->control->dev,
13841445
&dev_attr_iCountryCodeRelDate);
13851446
}
1447+
wake_up_all(&acm->wioctl);
13861448
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
13871449
usb_set_intfdata(acm->control, NULL);
13881450
usb_set_intfdata(acm->data, NULL);

drivers/usb/class/cdc-acm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ struct acm {
106106
struct work_struct work; /* work queue entry for line discipline waking up */
107107
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
108108
unsigned int ctrlout; /* output control lines (DTR, RTS) */
109+
struct async_icount iocount; /* counters for control line changes */
110+
struct async_icount oldcount; /* for comparison of counter */
111+
wait_queue_head_t wioctl; /* for ioctl */
109112
unsigned int writesize; /* max packet size for the output bulk endpoint */
110113
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
111114
unsigned int minor; /* acm minor number */

0 commit comments

Comments
 (0)