diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 116 |
1 files changed, 104 insertions, 12 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e8404319ca68..900f7ff805ee 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,72 @@ 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 | |||
856 | static int get_serial_usage(struct acm *acm, | ||
857 | struct serial_icounter_struct __user *count) | ||
858 | { | ||
859 | struct serial_icounter_struct icount; | ||
860 | int rv = 0; | ||
861 | |||
862 | memset(&icount, 0, sizeof(icount)); | ||
863 | icount.dsr = acm->iocount.dsr; | ||
864 | icount.rng = acm->iocount.rng; | ||
865 | icount.dcd = acm->iocount.dcd; | ||
866 | icount.frame = acm->iocount.frame; | ||
867 | icount.overrun = acm->iocount.overrun; | ||
868 | icount.parity = acm->iocount.parity; | ||
869 | icount.brk = acm->iocount.brk; | ||
870 | |||
871 | if (copy_to_user(count, &icount, sizeof(icount)) > 0) | ||
872 | rv = -EFAULT; | ||
873 | |||
874 | return rv; | ||
875 | } | ||
876 | |||
799 | static int acm_tty_ioctl(struct tty_struct *tty, | 877 | static int acm_tty_ioctl(struct tty_struct *tty, |
800 | unsigned int cmd, unsigned long arg) | 878 | unsigned int cmd, unsigned long arg) |
801 | { | 879 | { |
@@ -809,6 +887,18 @@ static int acm_tty_ioctl(struct tty_struct *tty, | |||
809 | case TIOCSSERIAL: | 887 | case TIOCSSERIAL: |
810 | rv = set_serial_info(acm, (struct serial_struct __user *) arg); | 888 | rv = set_serial_info(acm, (struct serial_struct __user *) arg); |
811 | break; | 889 | break; |
890 | case TIOCMIWAIT: | ||
891 | rv = usb_autopm_get_interface(acm->control); | ||
892 | if (rv < 0) { | ||
893 | rv = -EIO; | ||
894 | break; | ||
895 | } | ||
896 | rv = wait_serial_change(acm, arg); | ||
897 | usb_autopm_put_interface(acm->control); | ||
898 | break; | ||
899 | case TIOCGICOUNT: | ||
900 | rv = get_serial_usage(acm, (struct serial_icounter_struct __user *) arg); | ||
901 | break; | ||
812 | } | 902 | } |
813 | 903 | ||
814 | return rv; | 904 | return rv; |
@@ -1167,6 +1257,7 @@ made_compressed_probe: | |||
1167 | acm->readsize = readsize; | 1257 | acm->readsize = readsize; |
1168 | acm->rx_buflimit = num_rx_buf; | 1258 | acm->rx_buflimit = num_rx_buf; |
1169 | INIT_WORK(&acm->work, acm_softint); | 1259 | INIT_WORK(&acm->work, acm_softint); |
1260 | init_waitqueue_head(&acm->wioctl); | ||
1170 | spin_lock_init(&acm->write_lock); | 1261 | spin_lock_init(&acm->write_lock); |
1171 | spin_lock_init(&acm->read_lock); | 1262 | spin_lock_init(&acm->read_lock); |
1172 | mutex_init(&acm->mutex); | 1263 | mutex_init(&acm->mutex); |
@@ -1383,6 +1474,7 @@ static void acm_disconnect(struct usb_interface *intf) | |||
1383 | device_remove_file(&acm->control->dev, | 1474 | device_remove_file(&acm->control->dev, |
1384 | &dev_attr_iCountryCodeRelDate); | 1475 | &dev_attr_iCountryCodeRelDate); |
1385 | } | 1476 | } |
1477 | wake_up_all(&acm->wioctl); | ||
1386 | device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); | 1478 | device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); |
1387 | usb_set_intfdata(acm->control, NULL); | 1479 | usb_set_intfdata(acm->control, NULL); |
1388 | usb_set_intfdata(acm->data, NULL); | 1480 | usb_set_intfdata(acm->data, NULL); |