aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/cdc-acm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r--drivers/usb/class/cdc-acm.c116
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
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
856static 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
799static int acm_tty_ioctl(struct tty_struct *tty, 877static 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);