aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2013-11-20 05:35:34 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-03 13:23:46 -0500
commit5a6a62bdb9257aa74ab0ad2b2c8a33b0f9b17ce4 (patch)
treec39408f8477c5332d5aaadd0ee9c275eb03f5ffe
parent4e065b8bba731af296f963e0608e6ea7d98c044f (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.c86
-rw-r--r--drivers/usb/class/cdc-acm.h3
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
811static 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
799static int acm_tty_ioctl(struct tty_struct *tty, 856static 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 */