|
@@ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb)
|
|
|
struct usb_cdc_notification *dr = urb->transfer_buffer;
|
|
|
unsigned char *data;
|
|
|
int newctrl;
|
|
|
+ int difference;
|
|
|
int retval;
|
|
|
int status = urb->status;
|
|
|
|
|
@@ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb)
|
|
|
tty_port_tty_hangup(&acm->port, false);
|
|
|
}
|
|
|
|
|
|
+ difference = acm->ctrlin ^ newctrl;
|
|
|
+ spin_lock(&acm->read_lock);
|
|
|
acm->ctrlin = newctrl;
|
|
|
+ acm->oldcount = acm->iocount;
|
|
|
+
|
|
|
+ if (difference & ACM_CTRL_DSR)
|
|
|
+ acm->iocount.dsr++;
|
|
|
+ if (difference & ACM_CTRL_BRK)
|
|
|
+ acm->iocount.brk++;
|
|
|
+ if (difference & ACM_CTRL_RI)
|
|
|
+ acm->iocount.rng++;
|
|
|
+ if (difference & ACM_CTRL_DCD)
|
|
|
+ acm->iocount.dcd++;
|
|
|
+ if (difference & ACM_CTRL_FRAMING)
|
|
|
+ acm->iocount.frame++;
|
|
|
+ if (difference & ACM_CTRL_PARITY)
|
|
|
+ acm->iocount.parity++;
|
|
|
+ if (difference & ACM_CTRL_OVERRUN)
|
|
|
+ acm->iocount.overrun++;
|
|
|
+ spin_unlock(&acm->read_lock);
|
|
|
+
|
|
|
+ if (difference)
|
|
|
+ wake_up_all(&acm->wioctl);
|
|
|
|
|
|
- dev_dbg(&acm->control->dev,
|
|
|
- "%s - input control lines: dcd%c dsr%c break%c "
|
|
|
- "ring%c framing%c parity%c overrun%c\n",
|
|
|
- __func__,
|
|
|
- acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
|
|
|
- acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
|
|
|
- acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
|
|
|
- acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
|
|
|
- acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
|
|
|
- acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
|
|
|
- acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
|
|
|
- break;
|
|
|
+ break;
|
|
|
|
|
|
default:
|
|
|
dev_dbg(&acm->control->dev,
|
|
@@ -796,6 +808,51 @@ static int set_serial_info(struct acm *acm,
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
+static int wait_serial_change(struct acm *acm, unsigned long arg)
|
|
|
+{
|
|
|
+ int rv = 0;
|
|
|
+ DECLARE_WAITQUEUE(wait, current);
|
|
|
+ struct async_icount old, new;
|
|
|
+
|
|
|
+ if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD ))
|
|
|
+ return -EINVAL;
|
|
|
+ do {
|
|
|
+ spin_lock_irq(&acm->read_lock);
|
|
|
+ old = acm->oldcount;
|
|
|
+ new = acm->iocount;
|
|
|
+ acm->oldcount = new;
|
|
|
+ spin_unlock_irq(&acm->read_lock);
|
|
|
+
|
|
|
+ if ((arg & TIOCM_DSR) &&
|
|
|
+ old.dsr != new.dsr)
|
|
|
+ break;
|
|
|
+ if ((arg & TIOCM_CD) &&
|
|
|
+ old.dcd != new.dcd)
|
|
|
+ break;
|
|
|
+ if ((arg & TIOCM_RI) &&
|
|
|
+ old.rng != new.rng)
|
|
|
+ break;
|
|
|
+
|
|
|
+ add_wait_queue(&acm->wioctl, &wait);
|
|
|
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
|
+ schedule();
|
|
|
+ remove_wait_queue(&acm->wioctl, &wait);
|
|
|
+ if (acm->disconnected) {
|
|
|
+ if (arg & TIOCM_CD)
|
|
|
+ break;
|
|
|
+ else
|
|
|
+ rv = -ENODEV;
|
|
|
+ } else {
|
|
|
+ if (signal_pending(current))
|
|
|
+ rv = -ERESTARTSYS;
|
|
|
+ }
|
|
|
+ } while (!rv);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return rv;
|
|
|
+}
|
|
|
+
|
|
|
static int acm_tty_ioctl(struct tty_struct *tty,
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
{
|
|
@@ -809,6 +866,9 @@ static int acm_tty_ioctl(struct tty_struct *tty,
|
|
|
case TIOCSSERIAL:
|
|
|
rv = set_serial_info(acm, (struct serial_struct __user *) arg);
|
|
|
break;
|
|
|
+ case TIOCMIWAIT:
|
|
|
+ rv = wait_serial_change(acm, arg);
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
return rv;
|
|
@@ -1167,6 +1227,7 @@ made_compressed_probe:
|
|
|
acm->readsize = readsize;
|
|
|
acm->rx_buflimit = num_rx_buf;
|
|
|
INIT_WORK(&acm->work, acm_softint);
|
|
|
+ init_waitqueue_head(&acm->wioctl);
|
|
|
spin_lock_init(&acm->write_lock);
|
|
|
spin_lock_init(&acm->read_lock);
|
|
|
mutex_init(&acm->mutex);
|
|
@@ -1383,6 +1444,7 @@ static void acm_disconnect(struct usb_interface *intf)
|
|
|
device_remove_file(&acm->control->dev,
|
|
|
&dev_attr_iCountryCodeRelDate);
|
|
|
}
|
|
|
+ wake_up_all(&acm->wioctl);
|
|
|
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
|
|
|
usb_set_intfdata(acm->control, NULL);
|
|
|
usb_set_intfdata(acm->data, NULL);
|