diff options
Diffstat (limited to 'drivers/usb/core/devio.c')
| -rw-r--r-- | drivers/usb/core/devio.c | 67 | 
1 files changed, 40 insertions, 27 deletions
| diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0b59731c3021..4b0448c26810 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -501,6 +501,7 @@ static void async_completed(struct urb *urb)  	as->status = urb->status;  	signr = as->signr;  	if (signr) { +		memset(&sinfo, 0, sizeof(sinfo));  		sinfo.si_signo = as->signr;  		sinfo.si_errno = as->status;  		sinfo.si_code = SI_ASYNCIO; @@ -1689,7 +1690,7 @@ static struct async *reap_as(struct usb_dev_state *ps)  	for (;;) {  		__set_current_state(TASK_INTERRUPTIBLE);  		as = async_getcompleted(ps); -		if (as) +		if (as || !connected(ps))  			break;  		if (signal_pending(current))  			break; @@ -1712,7 +1713,7 @@ static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)  	}  	if (signal_pending(current))  		return -EINTR; -	return -EIO; +	return -ENODEV;  }  static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg) @@ -1721,10 +1722,11 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)  	struct async *as;  	as = async_getcompleted(ps); -	retval = -EAGAIN;  	if (as) {  		retval = processcompl(as, (void __user * __user *)arg);  		free_async(as); +	} else { +		retval = (connected(ps) ? -EAGAIN : -ENODEV);  	}  	return retval;  } @@ -1854,7 +1856,7 @@ static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)  	}  	if (signal_pending(current))  		return -EINTR; -	return -EIO; +	return -ENODEV;  }  static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg) @@ -1862,11 +1864,12 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar  	int retval;  	struct async *as; -	retval = -EAGAIN;  	as = async_getcompleted(ps);  	if (as) {  		retval = processcompl_compat(as, (void __user * __user *)arg);  		free_async(as); +	} else { +		retval = (connected(ps) ? -EAGAIN : -ENODEV);  	}  	return retval;  } @@ -2038,7 +2041,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)  {  	__u32 caps; -	caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; +	caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM | +			USBDEVFS_CAP_REAP_AFTER_DISCONNECT;  	if (!ps->dev->bus->no_stop_on_short)  		caps |= USBDEVFS_CAP_BULK_CONTINUATION;  	if (ps->dev->bus->sg_tablesize) @@ -2138,6 +2142,32 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,  		return -EPERM;  	usb_lock_device(dev); + +	/* Reap operations are allowed even after disconnection */ +	switch (cmd) { +	case USBDEVFS_REAPURB: +		snoop(&dev->dev, "%s: REAPURB\n", __func__); +		ret = proc_reapurb(ps, p); +		goto done; + +	case USBDEVFS_REAPURBNDELAY: +		snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__); +		ret = proc_reapurbnonblock(ps, p); +		goto done; + +#ifdef CONFIG_COMPAT +	case USBDEVFS_REAPURB32: +		snoop(&dev->dev, "%s: REAPURB32\n", __func__); +		ret = proc_reapurb_compat(ps, p); +		goto done; + +	case USBDEVFS_REAPURBNDELAY32: +		snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__); +		ret = proc_reapurbnonblock_compat(ps, p); +		goto done; +#endif +	} +  	if (!connected(ps)) {  		usb_unlock_device(dev);  		return -ENODEV; @@ -2231,16 +2261,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,  			inode->i_mtime = CURRENT_TIME;  		break; -	case USBDEVFS_REAPURB32: -		snoop(&dev->dev, "%s: REAPURB32\n", __func__); -		ret = proc_reapurb_compat(ps, p); -		break; - -	case USBDEVFS_REAPURBNDELAY32: -		snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__); -		ret = proc_reapurbnonblock_compat(ps, p); -		break; -  	case USBDEVFS_IOCTL32:  		snoop(&dev->dev, "%s: IOCTL32\n", __func__);  		ret = proc_ioctl_compat(ps, ptr_to_compat(p)); @@ -2252,16 +2272,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,  		ret = proc_unlinkurb(ps, p);  		break; -	case USBDEVFS_REAPURB: -		snoop(&dev->dev, "%s: REAPURB\n", __func__); -		ret = proc_reapurb(ps, p); -		break; - -	case USBDEVFS_REAPURBNDELAY: -		snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__); -		ret = proc_reapurbnonblock(ps, p); -		break; -  	case USBDEVFS_DISCSIGNAL:  		snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);  		ret = proc_disconnectsignal(ps, p); @@ -2304,6 +2314,8 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,  		ret = proc_free_streams(ps, p);  		break;  	} + + done:  	usb_unlock_device(dev);  	if (ret >= 0)  		inode->i_atime = CURRENT_TIME; @@ -2371,6 +2383,7 @@ static void usbdev_remove(struct usb_device *udev)  		wake_up_all(&ps->wait);  		list_del_init(&ps->list);  		if (ps->discsignr) { +			memset(&sinfo, 0, sizeof(sinfo));  			sinfo.si_signo = ps->discsignr;  			sinfo.si_errno = EPIPE;  			sinfo.si_code = SI_ASYNCIO; @@ -2395,7 +2408,7 @@ static int usbdev_notify(struct notifier_block *self,  }  static struct notifier_block usbdev_nb = { -	.notifier_call = 	usbdev_notify, +	.notifier_call =	usbdev_notify,  };  static struct cdev usb_device_cdev; |