diff options
Diffstat (limited to 'drivers/usb/class')
| -rw-r--r-- | drivers/usb/class/cdc-acm.c | 118 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-acm.h | 3 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-wdm.c | 78 | ||||
| -rw-r--r-- | drivers/usb/class/usblp.c | 1 | ||||
| -rw-r--r-- | drivers/usb/class/usbtmc.c | 1 | 
5 files changed, 150 insertions, 51 deletions
| diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 3e7560f004f..900f7ff805e 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); @@ -1515,6 +1607,8 @@ static int acm_reset_resume(struct usb_interface *intf)  static const struct usb_device_id acm_ids[] = {  	/* quirky and broken devices */ +	{ USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */ +	.driver_info = NO_UNION_NORMAL, },/* has no union descriptor */  	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */  	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */  	}, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 0f76e4af600..e38dc785808 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 4d387596f3f..a051a7a2b1b 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: @@ -854,13 +860,11 @@ static int wdm_manage_power(struct usb_interface *intf, int on)  {  	/* need autopm_get/put here to ensure the usbcore sees the new value */  	int rv = usb_autopm_get_interface(intf); -	if (rv < 0) -		goto err;  	intf->needs_remote_wakeup = on; -	usb_autopm_put_interface(intf); -err: -	return rv; +	if (!rv) +		usb_autopm_put_interface(intf); +	return 0;  }  static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d4c47d5d762..0924ee40a96 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 09de131ee0c..cfbec9c7e09 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> | 
