diff options
Diffstat (limited to 'drivers/usb/core/devio.c')
| -rw-r--r-- | drivers/usb/core/devio.c | 905 | 
1 files changed, 624 insertions, 281 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 045bb4b823e..257876ea03a 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -37,29 +37,32 @@  #include <linux/fs.h>  #include <linux/mm.h>  #include <linux/slab.h> -#include <linux/smp_lock.h>  #include <linux/signal.h>  #include <linux/poll.h>  #include <linux/module.h> +#include <linux/string.h>  #include <linux/usb.h>  #include <linux/usbdevice_fs.h>  #include <linux/usb/hcd.h>	/* for usbcore internals */  #include <linux/cdev.h>  #include <linux/notifier.h>  #include <linux/security.h> -#include <asm/uaccess.h> +#include <linux/user_namespace.h> +#include <linux/scatterlist.h> +#include <linux/uaccess.h>  #include <asm/byteorder.h>  #include <linux/moduleparam.h>  #include "usb.h"  #define USB_MAXBUS			64 -#define USB_DEVICE_MAX			USB_MAXBUS * 128 +#define USB_DEVICE_MAX			(USB_MAXBUS * 128) +#define USB_SG_SIZE			16384 /* split-size for large txs */  /* Mutual exclusion for removal, open, and release */  DEFINE_MUTEX(usbfs_mutex); -struct dev_state { +struct usb_dev_state {  	struct list_head list;      /* state list */  	struct usb_device *dev;  	struct file *file; @@ -69,7 +72,7 @@ struct dev_state {  	wait_queue_head_t wait;     /* wake up if a request completed */  	unsigned int discsignr;  	struct pid *disc_pid; -	uid_t disc_uid, disc_euid; +	const struct cred *cred;  	void __user *disccontext;  	unsigned long ifclaimed;  	u32 secid; @@ -78,21 +81,22 @@ struct dev_state {  struct async {  	struct list_head asynclist; -	struct dev_state *ps; +	struct usb_dev_state *ps;  	struct pid *pid; -	uid_t uid, euid; +	const struct cred *cred;  	unsigned int signr;  	unsigned int ifnum;  	void __user *userbuffer;  	void __user *userurb;  	struct urb *urb; +	unsigned int mem_usage;  	int status;  	u32 secid;  	u8 bulk_addr;  	u8 bulk_status;  }; -static int usbfs_snoop; +static bool usbfs_snoop;  module_param(usbfs_snoop, bool, S_IRUGO | S_IWUSR);  MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic"); @@ -108,10 +112,46 @@ enum snoop_when {  #define USB_DEVICE_DEV		MKDEV(USB_DEVICE_MAJOR, 0) -#define	MAX_USBFS_BUFFER_SIZE	16384 +/* Limit on the total amount of memory we can allocate for transfers */ +static unsigned usbfs_memory_mb = 16; +module_param(usbfs_memory_mb, uint, 0644); +MODULE_PARM_DESC(usbfs_memory_mb, +		"maximum MB allowed for usbfs buffers (0 = no limit)"); +/* Hard limit, necessary to avoid arithmetic overflow */ +#define USBFS_XFER_MAX		(UINT_MAX / 2 - 1000000) -static int connected(struct dev_state *ps) +static atomic_t usbfs_memory_usage;	/* Total memory currently allocated */ + +/* Check whether it's okay to allocate more memory for a transfer */ +static int usbfs_increase_memory_usage(unsigned amount) +{ +	unsigned lim; + +	/* +	 * Convert usbfs_memory_mb to bytes, avoiding overflows. +	 * 0 means use the hard limit (effectively unlimited). +	 */ +	lim = ACCESS_ONCE(usbfs_memory_mb); +	if (lim == 0 || lim > (USBFS_XFER_MAX >> 20)) +		lim = USBFS_XFER_MAX; +	else +		lim <<= 20; + +	atomic_add(amount, &usbfs_memory_usage); +	if (atomic_read(&usbfs_memory_usage) <= lim) +		return 0; +	atomic_sub(amount, &usbfs_memory_usage); +	return -ENOMEM; +} + +/* Memory for a transfer is being deallocated */ +static void usbfs_decrease_memory_usage(unsigned amount) +{ +	atomic_sub(amount, &usbfs_memory_usage); +} + +static int connected(struct usb_dev_state *ps)  {  	return (!list_empty(&ps->list) &&  			ps->dev->state != USB_STATE_NOTATTACHED); @@ -121,7 +161,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)  {  	loff_t ret; -	mutex_lock(&file->f_dentry->d_inode->i_mutex); +	mutex_lock(&file_inode(file)->i_mutex);  	switch (orig) {  	case 0: @@ -137,14 +177,14 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)  		ret = -EINVAL;  	} -	mutex_unlock(&file->f_dentry->d_inode->i_mutex); +	mutex_unlock(&file_inode(file)->i_mutex);  	return ret;  }  static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes,  			   loff_t *ppos)  { -	struct dev_state *ps = file->private_data; +	struct usb_dev_state *ps = file->private_data;  	struct usb_device *dev = ps->dev;  	ssize_t ret = 0;  	unsigned len; @@ -248,16 +288,26 @@ static struct async *alloc_async(unsigned int numisoframes)  static void free_async(struct async *as)  { +	int i; +  	put_pid(as->pid); +	if (as->cred) +		put_cred(as->cred); +	for (i = 0; i < as->urb->num_sgs; i++) { +		if (sg_page(&as->urb->sg[i])) +			kfree(sg_virt(&as->urb->sg[i])); +	} +	kfree(as->urb->sg);  	kfree(as->urb->transfer_buffer);  	kfree(as->urb->setup_packet);  	usb_free_urb(as->urb); +	usbfs_decrease_memory_usage(as->mem_usage);  	kfree(as);  }  static void async_newpending(struct async *as)  { -	struct dev_state *ps = as->ps; +	struct usb_dev_state *ps = as->ps;  	unsigned long flags;  	spin_lock_irqsave(&ps->lock, flags); @@ -267,7 +317,7 @@ static void async_newpending(struct async *as)  static void async_removepending(struct async *as)  { -	struct dev_state *ps = as->ps; +	struct usb_dev_state *ps = as->ps;  	unsigned long flags;  	spin_lock_irqsave(&ps->lock, flags); @@ -275,7 +325,7 @@ static void async_removepending(struct async *as)  	spin_unlock_irqrestore(&ps->lock, flags);  } -static struct async *async_getcompleted(struct dev_state *ps) +static struct async *async_getcompleted(struct usb_dev_state *ps)  {  	unsigned long flags;  	struct async *as = NULL; @@ -290,20 +340,17 @@ static struct async *async_getcompleted(struct dev_state *ps)  	return as;  } -static struct async *async_getpending(struct dev_state *ps, +static struct async *async_getpending(struct usb_dev_state *ps,  					     void __user *userurb)  { -	unsigned long flags;  	struct async *as; -	spin_lock_irqsave(&ps->lock, flags);  	list_for_each_entry(as, &ps->async_pending, asynclist)  		if (as->userurb == userurb) {  			list_del_init(&as->asynclist); -			spin_unlock_irqrestore(&ps->lock, flags);  			return as;  		} -	spin_unlock_irqrestore(&ps->lock, flags); +  	return NULL;  } @@ -351,13 +398,61 @@ static void snoop_urb(struct usb_device *udev,  	}  } +static void snoop_urb_data(struct urb *urb, unsigned len) +{ +	int i, size; + +	if (!usbfs_snoop) +		return; + +	if (urb->num_sgs == 0) { +		print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, +			urb->transfer_buffer, len, 1); +		return; +	} + +	for (i = 0; i < urb->num_sgs && len; i++) { +		size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; +		print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, +			sg_virt(&urb->sg[i]), size, 1); +		len -= size; +	} +} + +static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb) +{ +	unsigned i, len, size; + +	if (urb->number_of_packets > 0)		/* Isochronous */ +		len = urb->transfer_buffer_length; +	else					/* Non-Isoc */ +		len = urb->actual_length; + +	if (urb->num_sgs == 0) { +		if (copy_to_user(userbuffer, urb->transfer_buffer, len)) +			return -EFAULT; +		return 0; +	} + +	for (i = 0; i < urb->num_sgs && len; i++) { +		size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; +		if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size)) +			return -EFAULT; +		userbuffer += size; +		len -= size; +	} + +	return 0; +} +  #define AS_CONTINUATION	1  #define AS_UNLINK	2 -static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr) +static void cancel_bulk_urbs(struct usb_dev_state *ps, unsigned bulk_addr)  __releases(ps->lock)  __acquires(ps->lock)  { +	struct urb *urb;  	struct async *as;  	/* Mark all the pending URBs that match bulk_addr, up to but not @@ -380,8 +475,11 @@ __acquires(ps->lock)  	list_for_each_entry(as, &ps->async_pending, asynclist) {  		if (as->bulk_status == AS_UNLINK) {  			as->bulk_status = 0;		/* Only once */ +			urb = as->urb; +			usb_get_urb(urb);  			spin_unlock(&ps->lock);		/* Allow completions */ -			usb_unlink_urb(as->urb); +			usb_unlink_urb(urb); +			usb_put_urb(urb);  			spin_lock(&ps->lock);  			goto rescan;  		} @@ -391,12 +489,11 @@ __acquires(ps->lock)  static void async_completed(struct urb *urb)  {  	struct async *as = urb->context; -	struct dev_state *ps = as->ps; +	struct usb_dev_state *ps = as->ps;  	struct siginfo sinfo;  	struct pid *pid = NULL; -	uid_t uid = 0; -	uid_t euid = 0;  	u32 secid = 0; +	const struct cred *cred = NULL;  	int signr;  	spin_lock(&ps->lock); @@ -408,30 +505,33 @@ static void async_completed(struct urb *urb)  		sinfo.si_errno = as->status;  		sinfo.si_code = SI_ASYNCIO;  		sinfo.si_addr = as->userurb; -		pid = as->pid; -		uid = as->uid; -		euid = as->euid; +		pid = get_pid(as->pid); +		cred = get_cred(as->cred);  		secid = as->secid;  	}  	snoop(&urb->dev->dev, "urb complete\n");  	snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, -			as->status, COMPLETE, -			((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ? -				NULL : urb->transfer_buffer, urb->actual_length); +			as->status, COMPLETE, NULL, 0); +	if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN) +		snoop_urb_data(urb, urb->actual_length); +  	if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&  			as->status != -ENOENT)  		cancel_bulk_urbs(ps, as->bulk_addr);  	spin_unlock(&ps->lock); -	if (signr) -		kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, -				      euid, secid); +	if (signr) { +		kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid); +		put_pid(pid); +		put_cred(cred); +	}  	wake_up(&ps->wait);  } -static void destroy_async(struct dev_state *ps, struct list_head *list) +static void destroy_async(struct usb_dev_state *ps, struct list_head *list)  { +	struct urb *urb;  	struct async *as;  	unsigned long flags; @@ -439,16 +539,19 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)  	while (!list_empty(list)) {  		as = list_entry(list->next, struct async, asynclist);  		list_del_init(&as->asynclist); +		urb = as->urb; +		usb_get_urb(urb);  		/* drop the spinlock so the completion handler can run */  		spin_unlock_irqrestore(&ps->lock, flags); -		usb_kill_urb(as->urb); +		usb_kill_urb(urb); +		usb_put_urb(urb);  		spin_lock_irqsave(&ps->lock, flags);  	}  	spin_unlock_irqrestore(&ps->lock, flags);  } -static void destroy_async_on_interface(struct dev_state *ps, +static void destroy_async_on_interface(struct usb_dev_state *ps,  				       unsigned int ifnum)  {  	struct list_head *p, *q, hitlist; @@ -463,7 +566,7 @@ static void destroy_async_on_interface(struct dev_state *ps,  	destroy_async(ps, &hitlist);  } -static void destroy_all_async(struct dev_state *ps) +static void destroy_all_async(struct usb_dev_state *ps)  {  	destroy_async(ps, &ps->async_pending);  } @@ -482,7 +585,7 @@ static int driver_probe(struct usb_interface *intf,  static void driver_disconnect(struct usb_interface *intf)  { -	struct dev_state *ps = usb_get_intfdata(intf); +	struct usb_dev_state *ps = usb_get_intfdata(intf);  	unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber;  	if (!ps) @@ -525,7 +628,7 @@ struct usb_driver usbfs_driver = {  	.resume =	driver_resume,  }; -static int claimintf(struct dev_state *ps, unsigned int ifnum) +static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)  {  	struct usb_device *dev = ps->dev;  	struct usb_interface *intf; @@ -547,7 +650,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum)  	return err;  } -static int releaseintf(struct dev_state *ps, unsigned int ifnum) +static int releaseintf(struct usb_dev_state *ps, unsigned int ifnum)  {  	struct usb_device *dev;  	struct usb_interface *intf; @@ -567,7 +670,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)  	return err;  } -static int checkintf(struct dev_state *ps, unsigned int ifnum) +static int checkintf(struct usb_dev_state *ps, unsigned int ifnum)  {  	if (ps->dev->state != USB_STATE_CONFIGURED)  		return -EHOSTUNREACH; @@ -607,10 +710,11 @@ static int findintfep(struct usb_device *dev, unsigned int ep)  	return -ENOENT;  } -static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, -			   unsigned int index) +static int check_ctrlrecip(struct usb_dev_state *ps, unsigned int requesttype, +			   unsigned int request, unsigned int index)  {  	int ret = 0; +	struct usb_host_interface *alt_setting;  	if (ps->dev->state != USB_STATE_UNAUTHENTICATED  	 && ps->dev->state != USB_STATE_ADDRESS @@ -619,10 +723,41 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,  	if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))  		return 0; +	/* +	 * check for the special corner case 'get_device_id' in the printer +	 * class specification, which we always want to allow as it is used +	 * to query things like ink level, etc. +	 */ +	if (requesttype == 0xa1 && request == 0) { +		alt_setting = usb_find_alt_setting(ps->dev->actconfig, +						   index >> 8, index & 0xff); +		if (alt_setting +		 && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER) +			return 0; +	} +  	index &= 0xff;  	switch (requesttype & USB_RECIP_MASK) {  	case USB_RECIP_ENDPOINT: +		if ((index & ~USB_DIR_IN) == 0) +			return 0;  		ret = findintfep(ps->dev, index); +		if (ret < 0) { +			/* +			 * Some not fully compliant Win apps seem to get +			 * index wrong and have the endpoint number here +			 * rather than the endpoint address (with the +			 * correct direction). Win does let this through, +			 * so we'll not reject it here but leave it to +			 * the device to not break KVM. But we warn. +			 */ +			ret = findintfep(ps->dev, index ^ 0x80); +			if (ret >= 0) +				dev_info(&ps->dev->dev, +					"%s: process %i (%s) requesting ep %02x but needs %02x\n", +					__func__, task_pid_nr(current), +					current->comm, index, index ^ 0x80); +		}  		if (ret >= 0)  			ret = checkintf(ps, ret);  		break; @@ -634,6 +769,88 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,  	return ret;  } +static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev, +						     unsigned char ep) +{ +	if (ep & USB_ENDPOINT_DIR_MASK) +		return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK]; +	else +		return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK]; +} + +static int parse_usbdevfs_streams(struct usb_dev_state *ps, +				  struct usbdevfs_streams __user *streams, +				  unsigned int *num_streams_ret, +				  unsigned int *num_eps_ret, +				  struct usb_host_endpoint ***eps_ret, +				  struct usb_interface **intf_ret) +{ +	unsigned int i, num_streams, num_eps; +	struct usb_host_endpoint **eps; +	struct usb_interface *intf = NULL; +	unsigned char ep; +	int ifnum, ret; + +	if (get_user(num_streams, &streams->num_streams) || +	    get_user(num_eps, &streams->num_eps)) +		return -EFAULT; + +	if (num_eps < 1 || num_eps > USB_MAXENDPOINTS) +		return -EINVAL; + +	/* The XHCI controller allows max 2 ^ 16 streams */ +	if (num_streams_ret && (num_streams < 2 || num_streams > 65536)) +		return -EINVAL; + +	eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL); +	if (!eps) +		return -ENOMEM; + +	for (i = 0; i < num_eps; i++) { +		if (get_user(ep, &streams->eps[i])) { +			ret = -EFAULT; +			goto error; +		} +		eps[i] = ep_to_host_endpoint(ps->dev, ep); +		if (!eps[i]) { +			ret = -EINVAL; +			goto error; +		} + +		/* usb_alloc/free_streams operate on an usb_interface */ +		ifnum = findintfep(ps->dev, ep); +		if (ifnum < 0) { +			ret = ifnum; +			goto error; +		} + +		if (i == 0) { +			ret = checkintf(ps, ifnum); +			if (ret < 0) +				goto error; +			intf = usb_ifnum_to_if(ps->dev, ifnum); +		} else { +			/* Verify all eps belong to the same interface */ +			if (ifnum != intf->altsetting->desc.bInterfaceNumber) { +				ret = -EINVAL; +				goto error; +			} +		} +	} + +	if (num_streams_ret) +		*num_streams_ret = num_streams; +	*num_eps_ret = num_eps; +	*eps_ret = eps; +	*intf_ret = intf; + +	return 0; + +error: +	kfree(eps); +	return ret; +} +  static int match_devt(struct device *dev, void *data)  {  	return dev->devt == (dev_t) (unsigned long) data; @@ -656,12 +873,11 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt)  static int usbdev_open(struct inode *inode, struct file *file)  {  	struct usb_device *dev = NULL; -	struct dev_state *ps; -	const struct cred *cred = current_cred(); +	struct usb_dev_state *ps;  	int ret;  	ret = -ENOMEM; -	ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL); +	ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL);  	if (!ps)  		goto out_free_ps; @@ -674,17 +890,6 @@ static int usbdev_open(struct inode *inode, struct file *file)  	if (imajor(inode) == USB_DEVICE_MAJOR)  		dev = usbdev_lookup_by_devt(inode->i_rdev); -#ifdef CONFIG_USB_DEVICEFS -	/* procfs file */ -	if (!dev) { -		dev = inode->i_private; -		if (dev && dev->usbfs_dentry && -					dev->usbfs_dentry->d_inode == inode) -			usb_get_dev(dev); -		else -			dev = NULL; -	} -#endif  	mutex_unlock(&usbfs_mutex);  	if (!dev) @@ -707,8 +912,7 @@ static int usbdev_open(struct inode *inode, struct file *file)  	init_waitqueue_head(&ps->wait);  	ps->discsignr = 0;  	ps->disc_pid = get_pid(task_pid(current)); -	ps->disc_uid = cred->uid; -	ps->disc_euid = cred->euid; +	ps->cred = get_current_cred();  	ps->disccontext = NULL;  	ps->ifclaimed = 0;  	security_task_getsecid(current, &ps->secid); @@ -730,7 +934,7 @@ static int usbdev_open(struct inode *inode, struct file *file)  static int usbdev_release(struct inode *inode, struct file *file)  { -	struct dev_state *ps = file->private_data; +	struct usb_dev_state *ps = file->private_data;  	struct usb_device *dev = ps->dev;  	unsigned int ifnum;  	struct async *as; @@ -750,6 +954,7 @@ static int usbdev_release(struct inode *inode, struct file *file)  	usb_unlock_device(dev);  	usb_put_dev(dev);  	put_pid(ps->disc_pid); +	put_cred(ps->cred);  	as = async_getcompleted(ps);  	while (as) { @@ -760,7 +965,7 @@ static int usbdev_release(struct inode *inode, struct file *file)  	return 0;  } -static int proc_control(struct dev_state *ps, void __user *arg) +static int proc_control(struct usb_dev_state *ps, void __user *arg)  {  	struct usb_device *dev = ps->dev;  	struct usbdevfs_ctrltransfer ctrl; @@ -771,28 +976,33 @@ static int proc_control(struct dev_state *ps, void __user *arg)  	if (copy_from_user(&ctrl, arg, sizeof(ctrl)))  		return -EFAULT; -	ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex); +	ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest, +			      ctrl.wIndex);  	if (ret)  		return ret;  	wLength = ctrl.wLength;		/* To suppress 64k PAGE_SIZE warning */  	if (wLength > PAGE_SIZE)  		return -EINVAL; +	ret = usbfs_increase_memory_usage(PAGE_SIZE + sizeof(struct urb) + +			sizeof(struct usb_ctrlrequest)); +	if (ret) +		return ret;  	tbuf = (unsigned char *)__get_free_page(GFP_KERNEL); -	if (!tbuf) -		return -ENOMEM; +	if (!tbuf) { +		ret = -ENOMEM; +		goto done; +	}  	tmo = ctrl.timeout;  	snoop(&dev->dev, "control urb: bRequestType=%02x "  		"bRequest=%02x wValue=%04x "  		"wIndex=%04x wLength=%04x\n", -		ctrl.bRequestType, ctrl.bRequest, -		__le16_to_cpup(&ctrl.wValue), -		__le16_to_cpup(&ctrl.wIndex), -		__le16_to_cpup(&ctrl.wLength)); +		ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, +		ctrl.wIndex, ctrl.wLength);  	if (ctrl.bRequestType & 0x80) {  		if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,  					       ctrl.wLength)) { -			free_page((unsigned long)tbuf); -			return -EINVAL; +			ret = -EINVAL; +			goto done;  		}  		pipe = usb_rcvctrlpipe(dev, 0);  		snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, NULL, 0); @@ -803,18 +1013,18 @@ static int proc_control(struct dev_state *ps, void __user *arg)  				    tbuf, ctrl.wLength, tmo);  		usb_lock_device(dev);  		snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, -			tbuf, i); +			  tbuf, max(i, 0));  		if ((i > 0) && ctrl.wLength) {  			if (copy_to_user(ctrl.data, tbuf, i)) { -				free_page((unsigned long)tbuf); -				return -EFAULT; +				ret = -EFAULT; +				goto done;  			}  		}  	} else {  		if (ctrl.wLength) {  			if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) { -				free_page((unsigned long)tbuf); -				return -EFAULT; +				ret = -EFAULT; +				goto done;  			}  		}  		pipe = usb_sndctrlpipe(dev, 0); @@ -828,17 +1038,21 @@ static int proc_control(struct dev_state *ps, void __user *arg)  		usb_lock_device(dev);  		snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, NULL, 0);  	} -	free_page((unsigned long)tbuf);  	if (i < 0 && i != -EPIPE) {  		dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL "  			   "failed cmd %s rqt %u rq %u len %u ret %d\n",  			   current->comm, ctrl.bRequestType, ctrl.bRequest,  			   ctrl.wLength, i);  	} -	return i; +	ret = i; + done: +	free_page((unsigned long) tbuf); +	usbfs_decrease_memory_usage(PAGE_SIZE + sizeof(struct urb) + +			sizeof(struct usb_ctrlrequest)); +	return ret;  } -static int proc_bulk(struct dev_state *ps, void __user *arg) +static int proc_bulk(struct usb_dev_state *ps, void __user *arg)  {  	struct usb_device *dev = ps->dev;  	struct usbdevfs_bulktransfer bulk; @@ -862,15 +1076,20 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)  	if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))  		return -EINVAL;  	len1 = bulk.len; -	if (len1 > MAX_USBFS_BUFFER_SIZE) +	if (len1 >= USBFS_XFER_MAX)  		return -EINVAL; -	if (!(tbuf = kmalloc(len1, GFP_KERNEL))) -		return -ENOMEM; +	ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb)); +	if (ret) +		return ret; +	if (!(tbuf = kmalloc(len1, GFP_KERNEL))) { +		ret = -ENOMEM; +		goto done; +	}  	tmo = bulk.timeout;  	if (bulk.ep & 0x80) {  		if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { -			kfree(tbuf); -			return -EINVAL; +			ret = -EINVAL; +			goto done;  		}  		snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0); @@ -881,15 +1100,15 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)  		if (!i && len2) {  			if (copy_to_user(bulk.data, tbuf, len2)) { -				kfree(tbuf); -				return -EFAULT; +				ret = -EFAULT; +				goto done;  			}  		}  	} else {  		if (len1) {  			if (copy_from_user(tbuf, bulk.data, len1)) { -				kfree(tbuf); -				return -EFAULT; +				ret = -EFAULT; +				goto done;  			}  		}  		snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1); @@ -899,13 +1118,28 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)  		usb_lock_device(dev);  		snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0);  	} +	ret = (i < 0 ? i : len2); + done:  	kfree(tbuf); -	if (i < 0) -		return i; -	return len2; +	usbfs_decrease_memory_usage(len1 + sizeof(struct urb)); +	return ret; +} + +static void check_reset_of_active_ep(struct usb_device *udev, +		unsigned int epnum, char *ioctl_name) +{ +	struct usb_host_endpoint **eps; +	struct usb_host_endpoint *ep; + +	eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out; +	ep = eps[epnum & 0x0f]; +	if (ep && !list_empty(&ep->urb_list)) +		dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n", +				task_pid_nr(current), current->comm, +				ioctl_name, epnum);  } -static int proc_resetep(struct dev_state *ps, void __user *arg) +static int proc_resetep(struct usb_dev_state *ps, void __user *arg)  {  	unsigned int ep;  	int ret; @@ -918,11 +1152,12 @@ static int proc_resetep(struct dev_state *ps, void __user *arg)  	ret = checkintf(ps, ret);  	if (ret)  		return ret; +	check_reset_of_active_ep(ps->dev, ep, "RESETEP");  	usb_reset_endpoint(ps->dev, ep);  	return 0;  } -static int proc_clearhalt(struct dev_state *ps, void __user *arg) +static int proc_clearhalt(struct usb_dev_state *ps, void __user *arg)  {  	unsigned int ep;  	int pipe; @@ -936,6 +1171,7 @@ static int proc_clearhalt(struct dev_state *ps, void __user *arg)  	ret = checkintf(ps, ret);  	if (ret)  		return ret; +	check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT");  	if (ep & USB_DIR_IN)  		pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);  	else @@ -944,7 +1180,7 @@ static int proc_clearhalt(struct dev_state *ps, void __user *arg)  	return usb_clear_halt(ps->dev, pipe);  } -static int proc_getdriver(struct dev_state *ps, void __user *arg) +static int proc_getdriver(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_getdriver gd;  	struct usb_interface *intf; @@ -956,14 +1192,14 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)  	if (!intf || !intf->dev.driver)  		ret = -ENODATA;  	else { -		strncpy(gd.driver, intf->dev.driver->name, +		strlcpy(gd.driver, intf->dev.driver->name,  				sizeof(gd.driver));  		ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);  	}  	return ret;  } -static int proc_connectinfo(struct dev_state *ps, void __user *arg) +static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_connectinfo ci = {  		.devnum = ps->dev->devnum, @@ -975,12 +1211,12 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)  	return 0;  } -static int proc_resetdevice(struct dev_state *ps) +static int proc_resetdevice(struct usb_dev_state *ps)  {  	return usb_reset_device(ps->dev);  } -static int proc_setintf(struct dev_state *ps, void __user *arg) +static int proc_setintf(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_setinterface setintf;  	int ret; @@ -989,11 +1225,14 @@ static int proc_setintf(struct dev_state *ps, void __user *arg)  		return -EFAULT;  	if ((ret = checkintf(ps, setintf.interface)))  		return ret; + +	destroy_async_on_interface(ps, setintf.interface); +  	return usb_set_interface(ps->dev, setintf.interface,  			setintf.altsetting);  } -static int proc_setconfig(struct dev_state *ps, void __user *arg) +static int proc_setconfig(struct usb_dev_state *ps, void __user *arg)  {  	int u;  	int status = 0; @@ -1041,18 +1280,19 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg)  	return status;  } -static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, +static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,  			struct usbdevfs_iso_packet_desc __user *iso_frame_desc,  			void __user *arg)  {  	struct usbdevfs_iso_packet_desc *isopkt = NULL;  	struct usb_host_endpoint *ep; -	struct async *as; +	struct async *as = NULL;  	struct usb_ctrlrequest *dr = NULL; -	const struct cred *cred = current_cred();  	unsigned int u, totlen, isofrmlen; -	int ret, ifnum = -1; -	int is_in; +	int i, ret, is_in, num_sgs = 0, ifnum = -1; +	int number_of_packets = 0; +	unsigned int stream_id = 0; +	void *buf;  	if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |  				USBDEVFS_URB_SHORT_NOT_OK | @@ -1072,42 +1312,34 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  		if (ret)  			return ret;  	} -	if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) { -		is_in = 1; -		ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; -	} else { -		is_in = 0; -		ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; -	} +	ep = ep_to_host_endpoint(ps->dev, uurb->endpoint);  	if (!ep)  		return -ENOENT; +	is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0; + +	u = 0;  	switch(uurb->type) {  	case USBDEVFS_URB_TYPE_CONTROL:  		if (!usb_endpoint_xfer_control(&ep->desc))  			return -EINVAL; -		/* min 8 byte setup packet, -		 * max 8 byte setup plus an arbitrary data stage */ -		if (uurb->buffer_length < 8 || -		    uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE)) +		/* min 8 byte setup packet */ +		if (uurb->buffer_length < 8)  			return -EINVAL;  		dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);  		if (!dr)  			return -ENOMEM;  		if (copy_from_user(dr, uurb->buffer, 8)) { -			kfree(dr); -			return -EFAULT; +			ret = -EFAULT; +			goto error;  		}  		if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) { -			kfree(dr); -			return -EINVAL; +			ret = -EINVAL; +			goto error;  		} -		ret = check_ctrlrecip(ps, dr->bRequestType, +		ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,  				      le16_to_cpup(&dr->wIndex)); -		if (ret) { -			kfree(dr); -			return ret; -		} -		uurb->number_of_packets = 0; +		if (ret) +			goto error;  		uurb->buffer_length = le16_to_cpup(&dr->wLength);  		uurb->buffer += 8;  		if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) { @@ -1124,6 +1356,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  			__le16_to_cpup(&dr->wValue),  			__le16_to_cpup(&dr->wIndex),  			__le16_to_cpup(&dr->wLength)); +		u = sizeof(struct usb_ctrlrequest);  		break;  	case USBDEVFS_URB_TYPE_BULK: @@ -1136,18 +1369,17 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  			uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;  			goto interrupt_urb;  		} -		uurb->number_of_packets = 0; -		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) -			return -EINVAL; +		num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); +		if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) +			num_sgs = 0; +		if (ep->streams) +			stream_id = uurb->stream_id;  		break;  	case USBDEVFS_URB_TYPE_INTERRUPT:  		if (!usb_endpoint_xfer_int(&ep->desc))  			return -EINVAL;   interrupt_urb: -		uurb->number_of_packets = 0; -		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) -			return -EINVAL;  		break;  	case USBDEVFS_URB_TYPE_ISO: @@ -1157,63 +1389,113 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  			return -EINVAL;  		if (!usb_endpoint_xfer_isoc(&ep->desc))  			return -EINVAL; +		number_of_packets = uurb->number_of_packets;  		isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * -				   uurb->number_of_packets; +				   number_of_packets;  		if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))  			return -ENOMEM;  		if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { -			kfree(isopkt); -			return -EFAULT; +			ret = -EFAULT; +			goto error;  		} -		for (totlen = u = 0; u < uurb->number_of_packets; u++) { -			/* arbitrary limit, -			 * sufficient for USB 2.0 high-bandwidth iso */ -			if (isopkt[u].length > 8192) { -				kfree(isopkt); -				return -EINVAL; +		for (totlen = u = 0; u < number_of_packets; u++) { +			/* +			 * arbitrary limit need for USB 3.0 +			 * bMaxBurst (0~15 allowed, 1~16 packets) +			 * bmAttributes (bit 1:0, mult 0~2, 1~3 packets) +			 * sizemax: 1024 * 16 * 3 = 49152 +			 */ +			if (isopkt[u].length > 49152) { +				ret = -EINVAL; +				goto error;  			}  			totlen += isopkt[u].length;  		} -		/* 3072 * 64 microframes */ -		if (totlen > 196608) { -			kfree(isopkt); -			return -EINVAL; -		} +		u *= sizeof(struct usb_iso_packet_descriptor);  		uurb->buffer_length = totlen;  		break;  	default:  		return -EINVAL;  	} + +	if (uurb->buffer_length >= USBFS_XFER_MAX) { +		ret = -EINVAL; +		goto error; +	}  	if (uurb->buffer_length > 0 &&  			!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,  				uurb->buffer, uurb->buffer_length)) { -		kfree(isopkt); -		kfree(dr); -		return -EFAULT; +		ret = -EFAULT; +		goto error;  	} -	as = alloc_async(uurb->number_of_packets); +	as = alloc_async(number_of_packets);  	if (!as) { -		kfree(isopkt); -		kfree(dr); -		return -ENOMEM; +		ret = -ENOMEM; +		goto error;  	} -	if (uurb->buffer_length > 0) { + +	u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length + +	     num_sgs * sizeof(struct scatterlist); +	ret = usbfs_increase_memory_usage(u); +	if (ret) +		goto error; +	as->mem_usage = u; + +	if (num_sgs) { +		as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), +				      GFP_KERNEL); +		if (!as->urb->sg) { +			ret = -ENOMEM; +			goto error; +		} +		as->urb->num_sgs = num_sgs; +		sg_init_table(as->urb->sg, as->urb->num_sgs); + +		totlen = uurb->buffer_length; +		for (i = 0; i < as->urb->num_sgs; i++) { +			u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen; +			buf = kmalloc(u, GFP_KERNEL); +			if (!buf) { +				ret = -ENOMEM; +				goto error; +			} +			sg_set_buf(&as->urb->sg[i], buf, u); + +			if (!is_in) { +				if (copy_from_user(buf, uurb->buffer, u)) { +					ret = -EFAULT; +					goto error; +				} +				uurb->buffer += u; +			} +			totlen -= u; +		} +	} else if (uurb->buffer_length > 0) {  		as->urb->transfer_buffer = kmalloc(uurb->buffer_length,  				GFP_KERNEL);  		if (!as->urb->transfer_buffer) { -			kfree(isopkt); -			kfree(dr); -			free_async(as); -			return -ENOMEM; +			ret = -ENOMEM; +			goto error;  		} -		/* Isochronous input data may end up being discontiguous -		 * if some of the packets are short.  Clear the buffer so -		 * that the gaps don't leak kernel data to userspace. -		 */ -		if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO) + +		if (!is_in) { +			if (copy_from_user(as->urb->transfer_buffer, +					   uurb->buffer, +					   uurb->buffer_length)) { +				ret = -EFAULT; +				goto error; +			} +		} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { +			/* +			 * Isochronous input data may end up being +			 * discontiguous if some of the packets are short. +			 * Clear the buffer so that the gaps don't leak +			 * kernel data to userspace. +			 */  			memset(as->urb->transfer_buffer, 0,  					uurb->buffer_length); +		}  	}  	as->urb->dev = ps->dev;  	as->urb->pipe = (uurb->type << 30) | @@ -1239,8 +1521,10 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  	as->urb->transfer_buffer_length = uurb->buffer_length;  	as->urb->setup_packet = (unsigned char *)dr; +	dr = NULL;  	as->urb->start_frame = uurb->start_frame; -	as->urb->number_of_packets = uurb->number_of_packets; +	as->urb->number_of_packets = number_of_packets; +	as->urb->stream_id = stream_id;  	if (uurb->type == USBDEVFS_URB_TYPE_ISO ||  			ps->dev->speed == USB_SPEED_HIGH)  		as->urb->interval = 1 << min(15, ep->desc.bInterval - 1); @@ -1248,12 +1532,13 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  		as->urb->interval = ep->desc.bInterval;  	as->urb->context = as;  	as->urb->complete = async_completed; -	for (totlen = u = 0; u < uurb->number_of_packets; u++) { +	for (totlen = u = 0; u < number_of_packets; u++) {  		as->urb->iso_frame_desc[u].offset = totlen;  		as->urb->iso_frame_desc[u].length = isopkt[u].length;  		totlen += isopkt[u].length;  	}  	kfree(isopkt); +	isopkt = NULL;  	as->ps = ps;  	as->userurb = arg;  	if (is_in && uurb->buffer_length > 0) @@ -1263,20 +1548,14 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  	as->signr = uurb->signr;  	as->ifnum = ifnum;  	as->pid = get_pid(task_pid(current)); -	as->uid = cred->uid; -	as->euid = cred->euid; +	as->cred = get_current_cred();  	security_task_getsecid(current, &as->secid); -	if (!is_in && uurb->buffer_length > 0) { -		if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, -				uurb->buffer_length)) { -			free_async(as); -			return -EFAULT; -		} -	}  	snoop_urb(ps->dev, as->userurb, as->urb->pipe,  			as->urb->transfer_buffer_length, 0, SUBMIT, -			is_in ? NULL : as->urb->transfer_buffer, -				uurb->buffer_length); +			NULL, 0); +	if (!is_in) +		snoop_urb_data(as->urb, as->urb->transfer_buffer_length); +  	async_newpending(as);  	if (usb_endpoint_xfer_bulk(&ep->desc)) { @@ -1316,13 +1595,19 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  		snoop_urb(ps->dev, as->userurb, as->urb->pipe,  				0, ret, COMPLETE, NULL, 0);  		async_removepending(as); -		free_async(as); -		return ret; +		goto error;  	}  	return 0; + + error: +	kfree(isopkt); +	kfree(dr); +	if (as) +		free_async(as); +	return ret;  } -static int proc_submiturb(struct dev_state *ps, void __user *arg) +static int proc_submiturb(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_urb uurb; @@ -1334,14 +1619,26 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)  			arg);  } -static int proc_unlinkurb(struct dev_state *ps, void __user *arg) +static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg)  { +	struct urb *urb;  	struct async *as; +	unsigned long flags; +	spin_lock_irqsave(&ps->lock, flags);  	as = async_getpending(ps, arg); -	if (!as) +	if (!as) { +		spin_unlock_irqrestore(&ps->lock, flags);  		return -EINVAL; -	usb_kill_urb(as->urb); +	} + +	urb = as->urb; +	usb_get_urb(urb); +	spin_unlock_irqrestore(&ps->lock, flags); + +	usb_kill_urb(urb); +	usb_put_urb(urb); +  	return 0;  } @@ -1353,11 +1650,7 @@ static int processcompl(struct async *as, void __user * __user *arg)  	unsigned int i;  	if (as->userbuffer && urb->actual_length) { -		if (urb->number_of_packets > 0)		/* Isochronous */ -			i = urb->transfer_buffer_length; -		else					/* Non-Isoc */ -			i = urb->actual_length; -		if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) +		if (copy_urb_data_to_user(as->userbuffer, urb))  			goto err_out;  	}  	if (put_user(as->status, &userurb->status)) @@ -1386,7 +1679,7 @@ err_out:  	return -EFAULT;  } -static struct async *reap_as(struct dev_state *ps) +static struct async *reap_as(struct usb_dev_state *ps)  {  	DECLARE_WAITQUEUE(wait, current);  	struct async *as = NULL; @@ -1409,7 +1702,7 @@ static struct async *reap_as(struct dev_state *ps)  	return as;  } -static int proc_reapurb(struct dev_state *ps, void __user *arg) +static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)  {  	struct async *as = reap_as(ps);  	if (as) { @@ -1422,7 +1715,7 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg)  	return -EIO;  } -static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg) +static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)  {  	int retval;  	struct async *as; @@ -1437,37 +1730,37 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)  }  #ifdef CONFIG_COMPAT -static int proc_control_compat(struct dev_state *ps, +static int proc_control_compat(struct usb_dev_state *ps,  				struct usbdevfs_ctrltransfer32 __user *p32)  { -        struct usbdevfs_ctrltransfer __user *p; -        __u32 udata; -        p = compat_alloc_user_space(sizeof(*p)); -        if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) || -            get_user(udata, &p32->data) || +	struct usbdevfs_ctrltransfer __user *p; +	__u32 udata; +	p = compat_alloc_user_space(sizeof(*p)); +	if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) || +	    get_user(udata, &p32->data) ||  	    put_user(compat_ptr(udata), &p->data))  		return -EFAULT; -        return proc_control(ps, p); +	return proc_control(ps, p);  } -static int proc_bulk_compat(struct dev_state *ps, +static int proc_bulk_compat(struct usb_dev_state *ps,  			struct usbdevfs_bulktransfer32 __user *p32)  { -        struct usbdevfs_bulktransfer __user *p; -        compat_uint_t n; -        compat_caddr_t addr; +	struct usbdevfs_bulktransfer __user *p; +	compat_uint_t n; +	compat_caddr_t addr; -        p = compat_alloc_user_space(sizeof(*p)); +	p = compat_alloc_user_space(sizeof(*p)); -        if (get_user(n, &p32->ep) || put_user(n, &p->ep) || -            get_user(n, &p32->len) || put_user(n, &p->len) || -            get_user(n, &p32->timeout) || put_user(n, &p->timeout) || -            get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data)) -                return -EFAULT; +	if (get_user(n, &p32->ep) || put_user(n, &p->ep) || +	    get_user(n, &p32->len) || put_user(n, &p->len) || +	    get_user(n, &p32->timeout) || put_user(n, &p->timeout) || +	    get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data)) +		return -EFAULT; -        return proc_bulk(ps, p); +	return proc_bulk(ps, p);  } -static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg) +static int proc_disconnectsignal_compat(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_disconnectsignal32 ds; @@ -1505,7 +1798,7 @@ static int get_urb32(struct usbdevfs_urb *kurb,  	return 0;  } -static int proc_submiturb_compat(struct dev_state *ps, void __user *arg) +static int proc_submiturb_compat(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_urb uurb; @@ -1524,10 +1817,10 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)  	void __user *addr = as->userurb;  	unsigned int i; -	if (as->userbuffer && urb->actual_length) -		if (copy_to_user(as->userbuffer, urb->transfer_buffer, -				 urb->actual_length)) +	if (as->userbuffer && urb->actual_length) { +		if (copy_urb_data_to_user(as->userbuffer, urb))  			return -EFAULT; +	}  	if (put_user(as->status, &userurb->status))  		return -EFAULT;  	if (put_user(urb->actual_length, &userurb->actual_length)) @@ -1551,7 +1844,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)  	return 0;  } -static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) +static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)  {  	struct async *as = reap_as(ps);  	if (as) { @@ -1564,7 +1857,7 @@ static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)  	return -EIO;  } -static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg) +static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)  {  	int retval;  	struct async *as; @@ -1581,7 +1874,7 @@ static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)  #endif -static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) +static int proc_disconnectsignal(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_disconnectsignal ds; @@ -1592,7 +1885,7 @@ static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)  	return 0;  } -static int proc_claiminterface(struct dev_state *ps, void __user *arg) +static int proc_claiminterface(struct usb_dev_state *ps, void __user *arg)  {  	unsigned int ifnum; @@ -1601,7 +1894,7 @@ static int proc_claiminterface(struct dev_state *ps, void __user *arg)  	return claimintf(ps, ifnum);  } -static int proc_releaseinterface(struct dev_state *ps, void __user *arg) +static int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg)  {  	unsigned int ifnum;  	int ret; @@ -1614,7 +1907,7 @@ static int proc_releaseinterface(struct dev_state *ps, void __user *arg)  	return 0;  } -static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) +static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)  {  	int			size;  	void			*buf = NULL; @@ -1624,7 +1917,8 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)  	/* alloc buffer */  	if ((size = _IOC_SIZE(ctl->ioctl_code)) > 0) { -		if ((buf = kmalloc(size, GFP_KERNEL)) == NULL) +		buf = kmalloc(size, GFP_KERNEL); +		if (buf == NULL)  			return -ENOMEM;  		if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) {  			if (copy_from_user(buf, ctl->data, size)) { @@ -1689,7 +1983,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)  	return retval;  } -static int proc_ioctl_default(struct dev_state *ps, void __user *arg) +static int proc_ioctl_default(struct usb_dev_state *ps, void __user *arg)  {  	struct usbdevfs_ioctl	ctrl; @@ -1699,7 +1993,7 @@ static int proc_ioctl_default(struct dev_state *ps, void __user *arg)  }  #ifdef CONFIG_COMPAT -static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg) +static int proc_ioctl_compat(struct usb_dev_state *ps, compat_uptr_t arg)  {  	struct usbdevfs_ioctl32 __user *uioc;  	struct usbdevfs_ioctl ctrl; @@ -1717,7 +2011,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)  }  #endif -static int proc_claim_port(struct dev_state *ps, void __user *arg) +static int proc_claim_port(struct usb_dev_state *ps, void __user *arg)  {  	unsigned portnum;  	int rc; @@ -1731,7 +2025,7 @@ static int proc_claim_port(struct dev_state *ps, void __user *arg)  	return rc;  } -static int proc_release_port(struct dev_state *ps, void __user *arg) +static int proc_release_port(struct usb_dev_state *ps, void __user *arg)  {  	unsigned portnum; @@ -1740,6 +2034,93 @@ static int proc_release_port(struct dev_state *ps, void __user *arg)  	return usb_hub_release_port(ps->dev, portnum, ps);  } +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; +	if (!ps->dev->bus->no_stop_on_short) +		caps |= USBDEVFS_CAP_BULK_CONTINUATION; +	if (ps->dev->bus->sg_tablesize) +		caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER; + +	if (put_user(caps, (__u32 __user *)arg)) +		return -EFAULT; + +	return 0; +} + +static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg) +{ +	struct usbdevfs_disconnect_claim dc; +	struct usb_interface *intf; + +	if (copy_from_user(&dc, arg, sizeof(dc))) +		return -EFAULT; + +	intf = usb_ifnum_to_if(ps->dev, dc.interface); +	if (!intf) +		return -EINVAL; + +	if (intf->dev.driver) { +		struct usb_driver *driver = to_usb_driver(intf->dev.driver); + +		if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) && +				strncmp(dc.driver, intf->dev.driver->name, +					sizeof(dc.driver)) != 0) +			return -EBUSY; + +		if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) && +				strncmp(dc.driver, intf->dev.driver->name, +					sizeof(dc.driver)) == 0) +			return -EBUSY; + +		dev_dbg(&intf->dev, "disconnect by usbfs\n"); +		usb_driver_release_interface(driver, intf); +	} + +	return claimintf(ps, dc.interface); +} + +static int proc_alloc_streams(struct usb_dev_state *ps, void __user *arg) +{ +	unsigned num_streams, num_eps; +	struct usb_host_endpoint **eps; +	struct usb_interface *intf; +	int r; + +	r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps, +				   &eps, &intf); +	if (r) +		return r; + +	destroy_async_on_interface(ps, +				   intf->altsetting[0].desc.bInterfaceNumber); + +	r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL); +	kfree(eps); +	return r; +} + +static int proc_free_streams(struct usb_dev_state *ps, void __user *arg) +{ +	unsigned num_eps; +	struct usb_host_endpoint **eps; +	struct usb_interface *intf; +	int r; + +	r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf); +	if (r) +		return r; + +	destroy_async_on_interface(ps, +				   intf->altsetting[0].desc.bInterfaceNumber); + +	r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL); +	kfree(eps); +	return r; +} +  /*   * NOTE:  All requests here that have interface numbers as parameters   * are assuming that somehow the configuration has been prevented from @@ -1748,8 +2129,8 @@ static int proc_release_port(struct dev_state *ps, void __user *arg)  static long usbdev_do_ioctl(struct file *file, unsigned int cmd,  				void __user *p)  { -	struct dev_state *ps = file->private_data; -	struct inode *inode = file->f_path.dentry->d_inode; +	struct usb_dev_state *ps = file->private_data; +	struct inode *inode = file_inode(file);  	struct usb_device *dev = ps->dev;  	int ret = -ENOTTY; @@ -1910,6 +2291,18 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,  		snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);  		ret = proc_release_port(ps, p);  		break; +	case USBDEVFS_GET_CAPABILITIES: +		ret = proc_get_capabilities(ps, p); +		break; +	case USBDEVFS_DISCONNECT_CLAIM: +		ret = proc_disconnect_claim(ps, p); +		break; +	case USBDEVFS_ALLOC_STREAMS: +		ret = proc_alloc_streams(ps, p); +		break; +	case USBDEVFS_FREE_STREAMS: +		ret = proc_free_streams(ps, p); +		break;  	}  	usb_unlock_device(dev);  	if (ret >= 0) @@ -1943,7 +2336,7 @@ static long usbdev_compat_ioctl(struct file *file, unsigned int cmd,  static unsigned int usbdev_poll(struct file *file,  				struct poll_table_struct *wait)  { -	struct dev_state *ps = file->private_data; +	struct usb_dev_state *ps = file->private_data;  	unsigned int mask = 0;  	poll_wait(file, &ps->wait, wait); @@ -1969,11 +2362,11 @@ const struct file_operations usbdev_file_operations = {  static void usbdev_remove(struct usb_device *udev)  { -	struct dev_state *ps; +	struct usb_dev_state *ps;  	struct siginfo sinfo;  	while (!list_empty(&udev->filelist)) { -		ps = list_entry(udev->filelist.next, struct dev_state, list); +		ps = list_entry(udev->filelist.next, struct usb_dev_state, list);  		destroy_all_async(ps);  		wake_up_all(&ps->wait);  		list_del_init(&ps->list); @@ -1982,51 +2375,19 @@ static void usbdev_remove(struct usb_device *udev)  			sinfo.si_errno = EPIPE;  			sinfo.si_code = SI_ASYNCIO;  			sinfo.si_addr = ps->disccontext; -			kill_pid_info_as_uid(ps->discsignr, &sinfo, -					ps->disc_pid, ps->disc_uid, -					ps->disc_euid, ps->secid); +			kill_pid_info_as_cred(ps->discsignr, &sinfo, +					ps->disc_pid, ps->cred, ps->secid);  		}  	}  } -#ifdef CONFIG_USB_DEVICE_CLASS -static struct class *usb_classdev_class; - -static int usb_classdev_add(struct usb_device *dev) -{ -	struct device *cldev; - -	cldev = device_create(usb_classdev_class, &dev->dev, dev->dev.devt, -			      NULL, "usbdev%d.%d", dev->bus->busnum, -			      dev->devnum); -	if (IS_ERR(cldev)) -		return PTR_ERR(cldev); -	dev->usb_classdev = cldev; -	return 0; -} - -static void usb_classdev_remove(struct usb_device *dev) -{ -	if (dev->usb_classdev) -		device_unregister(dev->usb_classdev); -} - -#else -#define usb_classdev_add(dev)		0 -#define usb_classdev_remove(dev)	do {} while (0) - -#endif -  static int usbdev_notify(struct notifier_block *self,  			       unsigned long action, void *dev)  {  	switch (action) {  	case USB_DEVICE_ADD: -		if (usb_classdev_add(dev)) -			return NOTIFY_BAD;  		break;  	case USB_DEVICE_REMOVE: -		usb_classdev_remove(dev);  		usbdev_remove(dev);  		break;  	} @@ -2056,21 +2417,6 @@ int __init usb_devio_init(void)  		       USB_DEVICE_MAJOR);  		goto error_cdev;  	} -#ifdef CONFIG_USB_DEVICE_CLASS -	usb_classdev_class = class_create(THIS_MODULE, "usb_device"); -	if (IS_ERR(usb_classdev_class)) { -		printk(KERN_ERR "Unable to register usb_device class\n"); -		retval = PTR_ERR(usb_classdev_class); -		cdev_del(&usb_device_cdev); -		usb_classdev_class = NULL; -		goto out; -	} -	/* devices of this class shadow the major:minor of their parent -	 * device, so clear ->dev_kobj to prevent adding duplicate entries -	 * to /sys/dev -	 */ -	usb_classdev_class->dev_kobj = NULL; -#endif  	usb_register_notify(&usbdev_nb);  out:  	return retval; @@ -2083,9 +2429,6 @@ error_cdev:  void usb_devio_cleanup(void)  {  	usb_unregister_notify(&usbdev_nb); -#ifdef CONFIG_USB_DEVICE_CLASS -	class_destroy(usb_classdev_class); -#endif  	cdev_del(&usb_device_cdev);  	unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);  }  | 
