summaryrefslogtreecommitdiffstats
path: root/drivers/usb/class
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/class')
-rw-r--r--drivers/usb/class/cdc-acm.c116
-rw-r--r--drivers/usb/class/cdc-acm.h3
-rw-r--r--drivers/usb/class/cdc-wdm.c70
-rw-r--r--drivers/usb/class/usblp.c1
-rw-r--r--drivers/usb/class/usbtmc.c1
5 files changed, 145 insertions, 46 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e840431..900f7ff 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)
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,72 @@ 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 get_serial_usage(struct acm *acm,
+ struct serial_icounter_struct __user *count)
+{
+ struct serial_icounter_struct icount;
+ int rv = 0;
+
+ memset(&icount, 0, sizeof(icount));
+ icount.dsr = acm->iocount.dsr;
+ icount.rng = acm->iocount.rng;
+ icount.dcd = acm->iocount.dcd;
+ icount.frame = acm->iocount.frame;
+ icount.overrun = acm->iocount.overrun;
+ icount.parity = acm->iocount.parity;
+ icount.brk = acm->iocount.brk;
+
+ if (copy_to_user(count, &icount, sizeof(icount)) > 0)
+ rv = -EFAULT;
+
+ return rv;
+}
+
static int acm_tty_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -809,6 +887,18 @@ 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 = usb_autopm_get_interface(acm->control);
+ if (rv < 0) {
+ rv = -EIO;
+ break;
+ }
+ rv = wait_serial_change(acm, arg);
+ usb_autopm_put_interface(acm->control);
+ break;
+ case TIOCGICOUNT:
+ rv = get_serial_usage(acm, (struct serial_icounter_struct __user *) arg);
+ break;
}
return rv;
@@ -1167,6 +1257,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 +1474,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);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 0f76e4a..e38dc78 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -106,6 +106,9 @@ struct acm {
struct work_struct work; /* work queue entry for line discipline waking up */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */
+ struct async_icount iocount; /* counters for control line changes */
+ struct async_icount oldcount; /* for comparison of counter */
+ wait_queue_head_t wioctl; /* for ioctl */
unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int minor; /* acm minor number */
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 0b23a86..a051a7a 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -432,6 +432,38 @@ outnl:
return rv < 0 ? rv : count;
}
+/*
+ * clear WDM_READ flag and possibly submit the read urb if resp_count
+ * is non-zero.
+ *
+ * Called with desc->iuspin locked
+ */
+static int clear_wdm_read_flag(struct wdm_device *desc)
+{
+ int rv = 0;
+
+ clear_bit(WDM_READ, &desc->flags);
+
+ /* submit read urb only if the device is waiting for it */
+ if (!desc->resp_count || !--desc->resp_count)
+ goto out;
+
+ set_bit(WDM_RESPONDING, &desc->flags);
+ spin_unlock_irq(&desc->iuspin);
+ rv = usb_submit_urb(desc->response, GFP_KERNEL);
+ spin_lock_irq(&desc->iuspin);
+ if (rv) {
+ dev_err(&desc->intf->dev,
+ "usb_submit_urb failed with result %d\n", rv);
+
+ /* make sure the next notification trigger a submit */
+ clear_bit(WDM_RESPONDING, &desc->flags);
+ desc->resp_count = 0;
+ }
+out:
+ return rv;
+}
+
static ssize_t wdm_read
(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
@@ -503,8 +535,10 @@ retry:
if (!desc->reslength) { /* zero length read */
dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
- clear_bit(WDM_READ, &desc->flags);
+ rv = clear_wdm_read_flag(desc);
spin_unlock_irq(&desc->iuspin);
+ if (rv < 0)
+ goto err;
goto retry;
}
cntr = desc->length;
@@ -526,37 +560,9 @@ retry:
desc->length -= cntr;
/* in case we had outstanding data */
- if (!desc->length) {
- clear_bit(WDM_READ, &desc->flags);
-
- if (--desc->resp_count) {
- set_bit(WDM_RESPONDING, &desc->flags);
- spin_unlock_irq(&desc->iuspin);
-
- rv = usb_submit_urb(desc->response, GFP_KERNEL);
- if (rv) {
- dev_err(&desc->intf->dev,
- "%s: usb_submit_urb failed with result %d\n",
- __func__, rv);
- spin_lock_irq(&desc->iuspin);
- clear_bit(WDM_RESPONDING, &desc->flags);
- spin_unlock_irq(&desc->iuspin);
-
- if (rv == -ENOMEM) {
- rv = schedule_work(&desc->rxwork);
- if (rv)
- dev_err(&desc->intf->dev, "Cannot schedule work\n");
- } else {
- spin_lock_irq(&desc->iuspin);
- desc->resp_count = 0;
- spin_unlock_irq(&desc->iuspin);
- }
- }
- } else
- spin_unlock_irq(&desc->iuspin);
- } else
- spin_unlock_irq(&desc->iuspin);
-
+ if (!desc->length)
+ clear_wdm_read_flag(desc);
+ spin_unlock_irq(&desc->iuspin);
rv = cntr;
err:
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index d4c47d5..0924ee4 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -52,7 +52,6 @@
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/poll.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/lp.h>
#include <linux/mutex.h>
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 09de131..cfbec9c 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -21,7 +21,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
OpenPOWER on IntegriCloud