diff options
author | Oliver Neukum <oneukum@suse.de> | 2013-11-20 05:35:34 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-12-03 13:23:46 -0500 |
commit | 5a6a62bdb9257aa74ab0ad2b2c8a33b0f9b17ce4 (patch) | |
tree | c39408f8477c5332d5aaadd0ee9c275eb03f5ffe | |
parent | 4e065b8bba731af296f963e0608e6ea7d98c044f (diff) |
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>
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 86 | ||||
-rw-r--r-- | drivers/usb/class/cdc-acm.h | 3 |
2 files changed, 77 insertions, 12 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 3e7560f004f8..7c0051710a04 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb) | |||
262 | struct usb_cdc_notification *dr = urb->transfer_buffer; | 262 | struct usb_cdc_notification *dr = urb->transfer_buffer; |
263 | unsigned char *data; | 263 | unsigned char *data; |
264 | int newctrl; | 264 | int newctrl; |
265 | int difference; | ||
265 | int retval; | 266 | int retval; |
266 | int status = urb->status; | 267 | int status = urb->status; |
267 | 268 | ||
@@ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb) | |||
302 | tty_port_tty_hangup(&acm->port, false); | 303 | tty_port_tty_hangup(&acm->port, false); |
303 | } | 304 | } |
304 | 305 | ||
306 | difference = acm->ctrlin ^ newctrl; | ||
307 | spin_lock(&acm->read_lock); | ||
305 | acm->ctrlin = newctrl; | 308 | 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); | ||
306 | 329 | ||
307 | dev_dbg(&acm->control->dev, | 330 | break; |
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; | ||
319 | 331 | ||
320 | default: | 332 | default: |
321 | dev_dbg(&acm->control->dev, | 333 | dev_dbg(&acm->control->dev, |
@@ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm, | |||
796 | return retval; | 808 | return retval; |
797 | } | 809 | } |
798 | 810 | ||
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 | |||
799 | static int acm_tty_ioctl(struct tty_struct *tty, | 856 | static int acm_tty_ioctl(struct tty_struct *tty, |
800 | unsigned int cmd, unsigned long arg) | 857 | unsigned int cmd, unsigned long arg) |
801 | { | 858 | { |
@@ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty, | |||
809 | case TIOCSSERIAL: | 866 | case TIOCSSERIAL: |
810 | rv = set_serial_info(acm, (struct serial_struct __user *) arg); | 867 | rv = set_serial_info(acm, (struct serial_struct __user *) arg); |
811 | break; | 868 | break; |
869 | case TIOCMIWAIT: | ||
870 | rv = wait_serial_change(acm, arg); | ||
871 | break; | ||
812 | } | 872 | } |
813 | 873 | ||
814 | return rv; | 874 | return rv; |
@@ -1167,6 +1227,7 @@ made_compressed_probe: | |||
1167 | acm->readsize = readsize; | 1227 | acm->readsize = readsize; |
1168 | acm->rx_buflimit = num_rx_buf; | 1228 | acm->rx_buflimit = num_rx_buf; |
1169 | INIT_WORK(&acm->work, acm_softint); | 1229 | INIT_WORK(&acm->work, acm_softint); |
1230 | init_waitqueue_head(&acm->wioctl); | ||
1170 | spin_lock_init(&acm->write_lock); | 1231 | spin_lock_init(&acm->write_lock); |
1171 | spin_lock_init(&acm->read_lock); | 1232 | spin_lock_init(&acm->read_lock); |
1172 | mutex_init(&acm->mutex); | 1233 | mutex_init(&acm->mutex); |
@@ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf) | |||
1383 | device_remove_file(&acm->control->dev, | 1444 | device_remove_file(&acm->control->dev, |
1384 | &dev_attr_iCountryCodeRelDate); | 1445 | &dev_attr_iCountryCodeRelDate); |
1385 | } | 1446 | } |
1447 | wake_up_all(&acm->wioctl); | ||
1386 | device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); | 1448 | device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); |
1387 | usb_set_intfdata(acm->control, NULL); | 1449 | usb_set_intfdata(acm->control, NULL); |
1388 | usb_set_intfdata(acm->data, NULL); | 1450 | usb_set_intfdata(acm->data, NULL); |
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 0f76e4af600e..e38dc785808f 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h | |||
@@ -106,6 +106,9 @@ struct acm { | |||
106 | struct work_struct work; /* work queue entry for line discipline waking up */ | 106 | struct work_struct work; /* work queue entry for line discipline waking up */ |
107 | unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ | 107 | unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ |
108 | unsigned int ctrlout; /* output control lines (DTR, RTS) */ | 108 | 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 */ | ||
109 | unsigned int writesize; /* max packet size for the output bulk endpoint */ | 112 | unsigned int writesize; /* max packet size for the output bulk endpoint */ |
110 | unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ | 113 | unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ |
111 | unsigned int minor; /* acm minor number */ | 114 | unsigned int minor; /* acm minor number */ |