162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*****************************************************************************/
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/*
562306a36Sopenharmony_ci *      devio.c  --  User space communication with USB devices.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *      Copyright (C) 1999-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  This file implements the usbfs/x/y files, where
1062306a36Sopenharmony_ci *  x is the bus number and y the device number.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  It allows user space programs/"drivers" to communicate directly
1362306a36Sopenharmony_ci *  with USB devices without intervening kernel driver.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *  Revision history
1662306a36Sopenharmony_ci *    22.12.1999   0.1   Initial release (split from proc_usb.c)
1762306a36Sopenharmony_ci *    04.01.2000   0.2   Turned into its own filesystem
1862306a36Sopenharmony_ci *    30.09.2005   0.3   Fix user-triggerable oops in async URB delivery
1962306a36Sopenharmony_ci *    			 (CAN-2005-3055)
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*****************************************************************************/
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/fs.h>
2562306a36Sopenharmony_ci#include <linux/mm.h>
2662306a36Sopenharmony_ci#include <linux/sched/signal.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/signal.h>
2962306a36Sopenharmony_ci#include <linux/poll.h>
3062306a36Sopenharmony_ci#include <linux/module.h>
3162306a36Sopenharmony_ci#include <linux/string.h>
3262306a36Sopenharmony_ci#include <linux/usb.h>
3362306a36Sopenharmony_ci#include <linux/usbdevice_fs.h>
3462306a36Sopenharmony_ci#include <linux/usb/hcd.h>	/* for usbcore internals */
3562306a36Sopenharmony_ci#include <linux/usb/quirks.h>
3662306a36Sopenharmony_ci#include <linux/cdev.h>
3762306a36Sopenharmony_ci#include <linux/notifier.h>
3862306a36Sopenharmony_ci#include <linux/security.h>
3962306a36Sopenharmony_ci#include <linux/user_namespace.h>
4062306a36Sopenharmony_ci#include <linux/scatterlist.h>
4162306a36Sopenharmony_ci#include <linux/uaccess.h>
4262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
4362306a36Sopenharmony_ci#include <asm/byteorder.h>
4462306a36Sopenharmony_ci#include <linux/moduleparam.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include "usb.h"
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#ifdef CONFIG_PM
4962306a36Sopenharmony_ci#define MAYBE_CAP_SUSPEND	USBDEVFS_CAP_SUSPEND
5062306a36Sopenharmony_ci#else
5162306a36Sopenharmony_ci#define MAYBE_CAP_SUSPEND	0
5262306a36Sopenharmony_ci#endif
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define USB_MAXBUS			64
5562306a36Sopenharmony_ci#define USB_DEVICE_MAX			(USB_MAXBUS * 128)
5662306a36Sopenharmony_ci#define USB_SG_SIZE			16384 /* split-size for large txs */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Mutual exclusion for ps->list in resume vs. release and remove */
5962306a36Sopenharmony_cistatic DEFINE_MUTEX(usbfs_mutex);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct usb_dev_state {
6262306a36Sopenharmony_ci	struct list_head list;      /* state list */
6362306a36Sopenharmony_ci	struct usb_device *dev;
6462306a36Sopenharmony_ci	struct file *file;
6562306a36Sopenharmony_ci	spinlock_t lock;            /* protects the async urb lists */
6662306a36Sopenharmony_ci	struct list_head async_pending;
6762306a36Sopenharmony_ci	struct list_head async_completed;
6862306a36Sopenharmony_ci	struct list_head memory_list;
6962306a36Sopenharmony_ci	wait_queue_head_t wait;     /* wake up if a request completed */
7062306a36Sopenharmony_ci	wait_queue_head_t wait_for_resume;   /* wake up upon runtime resume */
7162306a36Sopenharmony_ci	unsigned int discsignr;
7262306a36Sopenharmony_ci	struct pid *disc_pid;
7362306a36Sopenharmony_ci	const struct cred *cred;
7462306a36Sopenharmony_ci	sigval_t disccontext;
7562306a36Sopenharmony_ci	unsigned long ifclaimed;
7662306a36Sopenharmony_ci	u32 disabled_bulk_eps;
7762306a36Sopenharmony_ci	unsigned long interface_allowed_mask;
7862306a36Sopenharmony_ci	int not_yet_resumed;
7962306a36Sopenharmony_ci	bool suspend_allowed;
8062306a36Sopenharmony_ci	bool privileges_dropped;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct usb_memory {
8462306a36Sopenharmony_ci	struct list_head memlist;
8562306a36Sopenharmony_ci	int vma_use_count;
8662306a36Sopenharmony_ci	int urb_use_count;
8762306a36Sopenharmony_ci	u32 size;
8862306a36Sopenharmony_ci	void *mem;
8962306a36Sopenharmony_ci	dma_addr_t dma_handle;
9062306a36Sopenharmony_ci	unsigned long vm_start;
9162306a36Sopenharmony_ci	struct usb_dev_state *ps;
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct async {
9562306a36Sopenharmony_ci	struct list_head asynclist;
9662306a36Sopenharmony_ci	struct usb_dev_state *ps;
9762306a36Sopenharmony_ci	struct pid *pid;
9862306a36Sopenharmony_ci	const struct cred *cred;
9962306a36Sopenharmony_ci	unsigned int signr;
10062306a36Sopenharmony_ci	unsigned int ifnum;
10162306a36Sopenharmony_ci	void __user *userbuffer;
10262306a36Sopenharmony_ci	void __user *userurb;
10362306a36Sopenharmony_ci	sigval_t userurb_sigval;
10462306a36Sopenharmony_ci	struct urb *urb;
10562306a36Sopenharmony_ci	struct usb_memory *usbm;
10662306a36Sopenharmony_ci	unsigned int mem_usage;
10762306a36Sopenharmony_ci	int status;
10862306a36Sopenharmony_ci	u8 bulk_addr;
10962306a36Sopenharmony_ci	u8 bulk_status;
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic bool usbfs_snoop;
11362306a36Sopenharmony_cimodule_param(usbfs_snoop, bool, S_IRUGO | S_IWUSR);
11462306a36Sopenharmony_ciMODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic unsigned usbfs_snoop_max = 65536;
11762306a36Sopenharmony_cimodule_param(usbfs_snoop_max, uint, S_IRUGO | S_IWUSR);
11862306a36Sopenharmony_ciMODULE_PARM_DESC(usbfs_snoop_max,
11962306a36Sopenharmony_ci		"maximum number of bytes to print while snooping");
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define snoop(dev, format, arg...)				\
12262306a36Sopenharmony_ci	do {							\
12362306a36Sopenharmony_ci		if (usbfs_snoop)				\
12462306a36Sopenharmony_ci			dev_info(dev, format, ## arg);		\
12562306a36Sopenharmony_ci	} while (0)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cienum snoop_when {
12862306a36Sopenharmony_ci	SUBMIT, COMPLETE
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define USB_DEVICE_DEV		MKDEV(USB_DEVICE_MAJOR, 0)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/* Limit on the total amount of memory we can allocate for transfers */
13462306a36Sopenharmony_cistatic u32 usbfs_memory_mb = 16;
13562306a36Sopenharmony_cimodule_param(usbfs_memory_mb, uint, 0644);
13662306a36Sopenharmony_ciMODULE_PARM_DESC(usbfs_memory_mb,
13762306a36Sopenharmony_ci		"maximum MB allowed for usbfs buffers (0 = no limit)");
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* Hard limit, necessary to avoid arithmetic overflow */
14062306a36Sopenharmony_ci#define USBFS_XFER_MAX         (UINT_MAX / 2 - 1000000)
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(usbfs_memory_usage_lock);
14362306a36Sopenharmony_cistatic u64 usbfs_memory_usage;	/* Total memory currently allocated */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/* Check whether it's okay to allocate more memory for a transfer */
14662306a36Sopenharmony_cistatic int usbfs_increase_memory_usage(u64 amount)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	u64 lim, total_mem;
14962306a36Sopenharmony_ci	unsigned long flags;
15062306a36Sopenharmony_ci	int ret;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	lim = READ_ONCE(usbfs_memory_mb);
15362306a36Sopenharmony_ci	lim <<= 20;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret = 0;
15662306a36Sopenharmony_ci	spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
15762306a36Sopenharmony_ci	total_mem = usbfs_memory_usage + amount;
15862306a36Sopenharmony_ci	if (lim > 0 && total_mem > lim)
15962306a36Sopenharmony_ci		ret = -ENOMEM;
16062306a36Sopenharmony_ci	else
16162306a36Sopenharmony_ci		usbfs_memory_usage = total_mem;
16262306a36Sopenharmony_ci	spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return ret;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* Memory for a transfer is being deallocated */
16862306a36Sopenharmony_cistatic void usbfs_decrease_memory_usage(u64 amount)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	unsigned long flags;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
17362306a36Sopenharmony_ci	if (amount > usbfs_memory_usage)
17462306a36Sopenharmony_ci		usbfs_memory_usage = 0;
17562306a36Sopenharmony_ci	else
17662306a36Sopenharmony_ci		usbfs_memory_usage -= amount;
17762306a36Sopenharmony_ci	spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int connected(struct usb_dev_state *ps)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return (!list_empty(&ps->list) &&
18362306a36Sopenharmony_ci			ps->dev->state != USB_STATE_NOTATTACHED);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct usb_dev_state *ps = usbm->ps;
18962306a36Sopenharmony_ci	struct usb_hcd *hcd = bus_to_hcd(ps->dev->bus);
19062306a36Sopenharmony_ci	unsigned long flags;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
19362306a36Sopenharmony_ci	--*count;
19462306a36Sopenharmony_ci	if (usbm->urb_use_count == 0 && usbm->vma_use_count == 0) {
19562306a36Sopenharmony_ci		list_del(&usbm->memlist);
19662306a36Sopenharmony_ci		spin_unlock_irqrestore(&ps->lock, flags);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		hcd_buffer_free_pages(hcd, usbm->size,
19962306a36Sopenharmony_ci				usbm->mem, usbm->dma_handle);
20062306a36Sopenharmony_ci		usbfs_decrease_memory_usage(
20162306a36Sopenharmony_ci			usbm->size + sizeof(struct usb_memory));
20262306a36Sopenharmony_ci		kfree(usbm);
20362306a36Sopenharmony_ci	} else {
20462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ps->lock, flags);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void usbdev_vm_open(struct vm_area_struct *vma)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct usb_memory *usbm = vma->vm_private_data;
21162306a36Sopenharmony_ci	unsigned long flags;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	spin_lock_irqsave(&usbm->ps->lock, flags);
21462306a36Sopenharmony_ci	++usbm->vma_use_count;
21562306a36Sopenharmony_ci	spin_unlock_irqrestore(&usbm->ps->lock, flags);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void usbdev_vm_close(struct vm_area_struct *vma)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct usb_memory *usbm = vma->vm_private_data;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic const struct vm_operations_struct usbdev_vm_ops = {
22662306a36Sopenharmony_ci	.open = usbdev_vm_open,
22762306a36Sopenharmony_ci	.close = usbdev_vm_close
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int usbdev_mmap(struct file *file, struct vm_area_struct *vma)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct usb_memory *usbm = NULL;
23362306a36Sopenharmony_ci	struct usb_dev_state *ps = file->private_data;
23462306a36Sopenharmony_ci	struct usb_hcd *hcd = bus_to_hcd(ps->dev->bus);
23562306a36Sopenharmony_ci	size_t size = vma->vm_end - vma->vm_start;
23662306a36Sopenharmony_ci	void *mem;
23762306a36Sopenharmony_ci	unsigned long flags;
23862306a36Sopenharmony_ci	dma_addr_t dma_handle = DMA_MAPPING_ERROR;
23962306a36Sopenharmony_ci	int ret;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory));
24262306a36Sopenharmony_ci	if (ret)
24362306a36Sopenharmony_ci		goto error;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	usbm = kzalloc(sizeof(struct usb_memory), GFP_KERNEL);
24662306a36Sopenharmony_ci	if (!usbm) {
24762306a36Sopenharmony_ci		ret = -ENOMEM;
24862306a36Sopenharmony_ci		goto error_decrease_mem;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	mem = hcd_buffer_alloc_pages(hcd,
25262306a36Sopenharmony_ci			size, GFP_USER | __GFP_NOWARN, &dma_handle);
25362306a36Sopenharmony_ci	if (!mem) {
25462306a36Sopenharmony_ci		ret = -ENOMEM;
25562306a36Sopenharmony_ci		goto error_free_usbm;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	memset(mem, 0, size);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	usbm->mem = mem;
26162306a36Sopenharmony_ci	usbm->dma_handle = dma_handle;
26262306a36Sopenharmony_ci	usbm->size = size;
26362306a36Sopenharmony_ci	usbm->ps = ps;
26462306a36Sopenharmony_ci	usbm->vm_start = vma->vm_start;
26562306a36Sopenharmony_ci	usbm->vma_use_count = 1;
26662306a36Sopenharmony_ci	INIT_LIST_HEAD(&usbm->memlist);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/*
26962306a36Sopenharmony_ci	 * In DMA-unavailable cases, hcd_buffer_alloc_pages allocates
27062306a36Sopenharmony_ci	 * normal pages and assigns DMA_MAPPING_ERROR to dma_handle. Check
27162306a36Sopenharmony_ci	 * whether we are in such cases, and then use remap_pfn_range (or
27262306a36Sopenharmony_ci	 * dma_mmap_coherent) to map normal (or DMA) pages into the user
27362306a36Sopenharmony_ci	 * space, respectively.
27462306a36Sopenharmony_ci	 */
27562306a36Sopenharmony_ci	if (dma_handle == DMA_MAPPING_ERROR) {
27662306a36Sopenharmony_ci		if (remap_pfn_range(vma, vma->vm_start,
27762306a36Sopenharmony_ci				    virt_to_phys(usbm->mem) >> PAGE_SHIFT,
27862306a36Sopenharmony_ci				    size, vma->vm_page_prot) < 0) {
27962306a36Sopenharmony_ci			dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
28062306a36Sopenharmony_ci			return -EAGAIN;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci	} else {
28362306a36Sopenharmony_ci		if (dma_mmap_coherent(hcd->self.sysdev, vma, mem, dma_handle,
28462306a36Sopenharmony_ci				      size)) {
28562306a36Sopenharmony_ci			dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
28662306a36Sopenharmony_ci			return -EAGAIN;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	vm_flags_set(vma, VM_IO | VM_DONTEXPAND | VM_DONTDUMP);
29162306a36Sopenharmony_ci	vma->vm_ops = &usbdev_vm_ops;
29262306a36Sopenharmony_ci	vma->vm_private_data = usbm;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
29562306a36Sopenharmony_ci	list_add_tail(&usbm->memlist, &ps->memory_list);
29662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cierror_free_usbm:
30162306a36Sopenharmony_ci	kfree(usbm);
30262306a36Sopenharmony_cierror_decrease_mem:
30362306a36Sopenharmony_ci	usbfs_decrease_memory_usage(size + sizeof(struct usb_memory));
30462306a36Sopenharmony_cierror:
30562306a36Sopenharmony_ci	return ret;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes,
30962306a36Sopenharmony_ci			   loff_t *ppos)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct usb_dev_state *ps = file->private_data;
31262306a36Sopenharmony_ci	struct usb_device *dev = ps->dev;
31362306a36Sopenharmony_ci	ssize_t ret = 0;
31462306a36Sopenharmony_ci	unsigned len;
31562306a36Sopenharmony_ci	loff_t pos;
31662306a36Sopenharmony_ci	int i;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	pos = *ppos;
31962306a36Sopenharmony_ci	usb_lock_device(dev);
32062306a36Sopenharmony_ci	if (!connected(ps)) {
32162306a36Sopenharmony_ci		ret = -ENODEV;
32262306a36Sopenharmony_ci		goto err;
32362306a36Sopenharmony_ci	} else if (pos < 0) {
32462306a36Sopenharmony_ci		ret = -EINVAL;
32562306a36Sopenharmony_ci		goto err;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (pos < sizeof(struct usb_device_descriptor)) {
32962306a36Sopenharmony_ci		/* 18 bytes - fits on the stack */
33062306a36Sopenharmony_ci		struct usb_device_descriptor temp_desc;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		memcpy(&temp_desc, &dev->descriptor, sizeof(dev->descriptor));
33362306a36Sopenharmony_ci		le16_to_cpus(&temp_desc.bcdUSB);
33462306a36Sopenharmony_ci		le16_to_cpus(&temp_desc.idVendor);
33562306a36Sopenharmony_ci		le16_to_cpus(&temp_desc.idProduct);
33662306a36Sopenharmony_ci		le16_to_cpus(&temp_desc.bcdDevice);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		len = sizeof(struct usb_device_descriptor) - pos;
33962306a36Sopenharmony_ci		if (len > nbytes)
34062306a36Sopenharmony_ci			len = nbytes;
34162306a36Sopenharmony_ci		if (copy_to_user(buf, ((char *)&temp_desc) + pos, len)) {
34262306a36Sopenharmony_ci			ret = -EFAULT;
34362306a36Sopenharmony_ci			goto err;
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		*ppos += len;
34762306a36Sopenharmony_ci		buf += len;
34862306a36Sopenharmony_ci		nbytes -= len;
34962306a36Sopenharmony_ci		ret += len;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	pos = sizeof(struct usb_device_descriptor);
35362306a36Sopenharmony_ci	for (i = 0; nbytes && i < dev->descriptor.bNumConfigurations; i++) {
35462306a36Sopenharmony_ci		struct usb_config_descriptor *config =
35562306a36Sopenharmony_ci			(struct usb_config_descriptor *)dev->rawdescriptors[i];
35662306a36Sopenharmony_ci		unsigned int length = le16_to_cpu(config->wTotalLength);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		if (*ppos < pos + length) {
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci			/* The descriptor may claim to be longer than it
36162306a36Sopenharmony_ci			 * really is.  Here is the actual allocated length. */
36262306a36Sopenharmony_ci			unsigned alloclen =
36362306a36Sopenharmony_ci				le16_to_cpu(dev->config[i].desc.wTotalLength);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci			len = length - (*ppos - pos);
36662306a36Sopenharmony_ci			if (len > nbytes)
36762306a36Sopenharmony_ci				len = nbytes;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci			/* Simply don't write (skip over) unallocated parts */
37062306a36Sopenharmony_ci			if (alloclen > (*ppos - pos)) {
37162306a36Sopenharmony_ci				alloclen -= (*ppos - pos);
37262306a36Sopenharmony_ci				if (copy_to_user(buf,
37362306a36Sopenharmony_ci				    dev->rawdescriptors[i] + (*ppos - pos),
37462306a36Sopenharmony_ci				    min(len, alloclen))) {
37562306a36Sopenharmony_ci					ret = -EFAULT;
37662306a36Sopenharmony_ci					goto err;
37762306a36Sopenharmony_ci				}
37862306a36Sopenharmony_ci			}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci			*ppos += len;
38162306a36Sopenharmony_ci			buf += len;
38262306a36Sopenharmony_ci			nbytes -= len;
38362306a36Sopenharmony_ci			ret += len;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		pos += length;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cierr:
39062306a36Sopenharmony_ci	usb_unlock_device(dev);
39162306a36Sopenharmony_ci	return ret;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/*
39562306a36Sopenharmony_ci * async list handling
39662306a36Sopenharmony_ci */
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic struct async *alloc_async(unsigned int numisoframes)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct async *as;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	as = kzalloc(sizeof(struct async), GFP_KERNEL);
40362306a36Sopenharmony_ci	if (!as)
40462306a36Sopenharmony_ci		return NULL;
40562306a36Sopenharmony_ci	as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
40662306a36Sopenharmony_ci	if (!as->urb) {
40762306a36Sopenharmony_ci		kfree(as);
40862306a36Sopenharmony_ci		return NULL;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	return as;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void free_async(struct async *as)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	int i;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	put_pid(as->pid);
41862306a36Sopenharmony_ci	if (as->cred)
41962306a36Sopenharmony_ci		put_cred(as->cred);
42062306a36Sopenharmony_ci	for (i = 0; i < as->urb->num_sgs; i++) {
42162306a36Sopenharmony_ci		if (sg_page(&as->urb->sg[i]))
42262306a36Sopenharmony_ci			kfree(sg_virt(&as->urb->sg[i]));
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	kfree(as->urb->sg);
42662306a36Sopenharmony_ci	if (as->usbm == NULL)
42762306a36Sopenharmony_ci		kfree(as->urb->transfer_buffer);
42862306a36Sopenharmony_ci	else
42962306a36Sopenharmony_ci		dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	kfree(as->urb->setup_packet);
43262306a36Sopenharmony_ci	usb_free_urb(as->urb);
43362306a36Sopenharmony_ci	usbfs_decrease_memory_usage(as->mem_usage);
43462306a36Sopenharmony_ci	kfree(as);
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic void async_newpending(struct async *as)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct usb_dev_state *ps = as->ps;
44062306a36Sopenharmony_ci	unsigned long flags;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
44362306a36Sopenharmony_ci	list_add_tail(&as->asynclist, &ps->async_pending);
44462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void async_removepending(struct async *as)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct usb_dev_state *ps = as->ps;
45062306a36Sopenharmony_ci	unsigned long flags;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
45362306a36Sopenharmony_ci	list_del_init(&as->asynclist);
45462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic struct async *async_getcompleted(struct usb_dev_state *ps)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	unsigned long flags;
46062306a36Sopenharmony_ci	struct async *as = NULL;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
46362306a36Sopenharmony_ci	if (!list_empty(&ps->async_completed)) {
46462306a36Sopenharmony_ci		as = list_entry(ps->async_completed.next, struct async,
46562306a36Sopenharmony_ci				asynclist);
46662306a36Sopenharmony_ci		list_del_init(&as->asynclist);
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
46962306a36Sopenharmony_ci	return as;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic struct async *async_getpending(struct usb_dev_state *ps,
47362306a36Sopenharmony_ci					     void __user *userurb)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct async *as;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	list_for_each_entry(as, &ps->async_pending, asynclist)
47862306a36Sopenharmony_ci		if (as->userurb == userurb) {
47962306a36Sopenharmony_ci			list_del_init(&as->asynclist);
48062306a36Sopenharmony_ci			return as;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	return NULL;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void snoop_urb(struct usb_device *udev,
48762306a36Sopenharmony_ci		void __user *userurb, int pipe, unsigned length,
48862306a36Sopenharmony_ci		int timeout_or_status, enum snoop_when when,
48962306a36Sopenharmony_ci		unsigned char *data, unsigned data_len)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	static const char *types[] = {"isoc", "int", "ctrl", "bulk"};
49262306a36Sopenharmony_ci	static const char *dirs[] = {"out", "in"};
49362306a36Sopenharmony_ci	int ep;
49462306a36Sopenharmony_ci	const char *t, *d;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!usbfs_snoop)
49762306a36Sopenharmony_ci		return;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ep = usb_pipeendpoint(pipe);
50062306a36Sopenharmony_ci	t = types[usb_pipetype(pipe)];
50162306a36Sopenharmony_ci	d = dirs[!!usb_pipein(pipe)];
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (userurb) {		/* Async */
50462306a36Sopenharmony_ci		if (when == SUBMIT)
50562306a36Sopenharmony_ci			dev_info(&udev->dev, "userurb %px, ep%d %s-%s, "
50662306a36Sopenharmony_ci					"length %u\n",
50762306a36Sopenharmony_ci					userurb, ep, t, d, length);
50862306a36Sopenharmony_ci		else
50962306a36Sopenharmony_ci			dev_info(&udev->dev, "userurb %px, ep%d %s-%s, "
51062306a36Sopenharmony_ci					"actual_length %u status %d\n",
51162306a36Sopenharmony_ci					userurb, ep, t, d, length,
51262306a36Sopenharmony_ci					timeout_or_status);
51362306a36Sopenharmony_ci	} else {
51462306a36Sopenharmony_ci		if (when == SUBMIT)
51562306a36Sopenharmony_ci			dev_info(&udev->dev, "ep%d %s-%s, length %u, "
51662306a36Sopenharmony_ci					"timeout %d\n",
51762306a36Sopenharmony_ci					ep, t, d, length, timeout_or_status);
51862306a36Sopenharmony_ci		else
51962306a36Sopenharmony_ci			dev_info(&udev->dev, "ep%d %s-%s, actual_length %u, "
52062306a36Sopenharmony_ci					"status %d\n",
52162306a36Sopenharmony_ci					ep, t, d, length, timeout_or_status);
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	data_len = min(data_len, usbfs_snoop_max);
52562306a36Sopenharmony_ci	if (data && data_len > 0) {
52662306a36Sopenharmony_ci		print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
52762306a36Sopenharmony_ci			data, data_len, 1);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic void snoop_urb_data(struct urb *urb, unsigned len)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	int i, size;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	len = min(len, usbfs_snoop_max);
53662306a36Sopenharmony_ci	if (!usbfs_snoop || len == 0)
53762306a36Sopenharmony_ci		return;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (urb->num_sgs == 0) {
54062306a36Sopenharmony_ci		print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
54162306a36Sopenharmony_ci			urb->transfer_buffer, len, 1);
54262306a36Sopenharmony_ci		return;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	for (i = 0; i < urb->num_sgs && len; i++) {
54662306a36Sopenharmony_ci		size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
54762306a36Sopenharmony_ci		print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
54862306a36Sopenharmony_ci			sg_virt(&urb->sg[i]), size, 1);
54962306a36Sopenharmony_ci		len -= size;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	unsigned i, len, size;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (urb->number_of_packets > 0)		/* Isochronous */
55862306a36Sopenharmony_ci		len = urb->transfer_buffer_length;
55962306a36Sopenharmony_ci	else					/* Non-Isoc */
56062306a36Sopenharmony_ci		len = urb->actual_length;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (urb->num_sgs == 0) {
56362306a36Sopenharmony_ci		if (copy_to_user(userbuffer, urb->transfer_buffer, len))
56462306a36Sopenharmony_ci			return -EFAULT;
56562306a36Sopenharmony_ci		return 0;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	for (i = 0; i < urb->num_sgs && len; i++) {
56962306a36Sopenharmony_ci		size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
57062306a36Sopenharmony_ci		if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size))
57162306a36Sopenharmony_ci			return -EFAULT;
57262306a36Sopenharmony_ci		userbuffer += size;
57362306a36Sopenharmony_ci		len -= size;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return 0;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci#define AS_CONTINUATION	1
58062306a36Sopenharmony_ci#define AS_UNLINK	2
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic void cancel_bulk_urbs(struct usb_dev_state *ps, unsigned bulk_addr)
58362306a36Sopenharmony_ci__releases(ps->lock)
58462306a36Sopenharmony_ci__acquires(ps->lock)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct urb *urb;
58762306a36Sopenharmony_ci	struct async *as;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Mark all the pending URBs that match bulk_addr, up to but not
59062306a36Sopenharmony_ci	 * including the first one without AS_CONTINUATION.  If such an
59162306a36Sopenharmony_ci	 * URB is encountered then a new transfer has already started so
59262306a36Sopenharmony_ci	 * the endpoint doesn't need to be disabled; otherwise it does.
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci	list_for_each_entry(as, &ps->async_pending, asynclist) {
59562306a36Sopenharmony_ci		if (as->bulk_addr == bulk_addr) {
59662306a36Sopenharmony_ci			if (as->bulk_status != AS_CONTINUATION)
59762306a36Sopenharmony_ci				goto rescan;
59862306a36Sopenharmony_ci			as->bulk_status = AS_UNLINK;
59962306a36Sopenharmony_ci			as->bulk_addr = 0;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci	ps->disabled_bulk_eps |= (1 << bulk_addr);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	/* Now carefully unlink all the marked pending URBs */
60562306a36Sopenharmony_ci rescan:
60662306a36Sopenharmony_ci	list_for_each_entry_reverse(as, &ps->async_pending, asynclist) {
60762306a36Sopenharmony_ci		if (as->bulk_status == AS_UNLINK) {
60862306a36Sopenharmony_ci			as->bulk_status = 0;		/* Only once */
60962306a36Sopenharmony_ci			urb = as->urb;
61062306a36Sopenharmony_ci			usb_get_urb(urb);
61162306a36Sopenharmony_ci			spin_unlock(&ps->lock);		/* Allow completions */
61262306a36Sopenharmony_ci			usb_unlink_urb(urb);
61362306a36Sopenharmony_ci			usb_put_urb(urb);
61462306a36Sopenharmony_ci			spin_lock(&ps->lock);
61562306a36Sopenharmony_ci			goto rescan;
61662306a36Sopenharmony_ci		}
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic void async_completed(struct urb *urb)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct async *as = urb->context;
62362306a36Sopenharmony_ci	struct usb_dev_state *ps = as->ps;
62462306a36Sopenharmony_ci	struct pid *pid = NULL;
62562306a36Sopenharmony_ci	const struct cred *cred = NULL;
62662306a36Sopenharmony_ci	unsigned long flags;
62762306a36Sopenharmony_ci	sigval_t addr;
62862306a36Sopenharmony_ci	int signr, errno;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
63162306a36Sopenharmony_ci	list_move_tail(&as->asynclist, &ps->async_completed);
63262306a36Sopenharmony_ci	as->status = urb->status;
63362306a36Sopenharmony_ci	signr = as->signr;
63462306a36Sopenharmony_ci	if (signr) {
63562306a36Sopenharmony_ci		errno = as->status;
63662306a36Sopenharmony_ci		addr = as->userurb_sigval;
63762306a36Sopenharmony_ci		pid = get_pid(as->pid);
63862306a36Sopenharmony_ci		cred = get_cred(as->cred);
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci	snoop(&urb->dev->dev, "urb complete\n");
64162306a36Sopenharmony_ci	snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
64262306a36Sopenharmony_ci			as->status, COMPLETE, NULL, 0);
64362306a36Sopenharmony_ci	if (usb_urb_dir_in(urb))
64462306a36Sopenharmony_ci		snoop_urb_data(urb, urb->actual_length);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
64762306a36Sopenharmony_ci			as->status != -ENOENT)
64862306a36Sopenharmony_ci		cancel_bulk_urbs(ps, as->bulk_addr);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	wake_up(&ps->wait);
65162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (signr) {
65462306a36Sopenharmony_ci		kill_pid_usb_asyncio(signr, errno, addr, pid, cred);
65562306a36Sopenharmony_ci		put_pid(pid);
65662306a36Sopenharmony_ci		put_cred(cred);
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic void destroy_async(struct usb_dev_state *ps, struct list_head *list)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct urb *urb;
66362306a36Sopenharmony_ci	struct async *as;
66462306a36Sopenharmony_ci	unsigned long flags;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
66762306a36Sopenharmony_ci	while (!list_empty(list)) {
66862306a36Sopenharmony_ci		as = list_last_entry(list, struct async, asynclist);
66962306a36Sopenharmony_ci		list_del_init(&as->asynclist);
67062306a36Sopenharmony_ci		urb = as->urb;
67162306a36Sopenharmony_ci		usb_get_urb(urb);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		/* drop the spinlock so the completion handler can run */
67462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ps->lock, flags);
67562306a36Sopenharmony_ci		usb_kill_urb(urb);
67662306a36Sopenharmony_ci		usb_put_urb(urb);
67762306a36Sopenharmony_ci		spin_lock_irqsave(&ps->lock, flags);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic void destroy_async_on_interface(struct usb_dev_state *ps,
68362306a36Sopenharmony_ci				       unsigned int ifnum)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct list_head *p, *q, hitlist;
68662306a36Sopenharmony_ci	unsigned long flags;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	INIT_LIST_HEAD(&hitlist);
68962306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
69062306a36Sopenharmony_ci	list_for_each_safe(p, q, &ps->async_pending)
69162306a36Sopenharmony_ci		if (ifnum == list_entry(p, struct async, asynclist)->ifnum)
69262306a36Sopenharmony_ci			list_move_tail(p, &hitlist);
69362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
69462306a36Sopenharmony_ci	destroy_async(ps, &hitlist);
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic void destroy_all_async(struct usb_dev_state *ps)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	destroy_async(ps, &ps->async_pending);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci/*
70362306a36Sopenharmony_ci * interface claims are made only at the request of user level code,
70462306a36Sopenharmony_ci * which can also release them (explicitly or by closing files).
70562306a36Sopenharmony_ci * they're also undone when devices disconnect.
70662306a36Sopenharmony_ci */
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic int driver_probe(struct usb_interface *intf,
70962306a36Sopenharmony_ci			const struct usb_device_id *id)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	return -ENODEV;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic void driver_disconnect(struct usb_interface *intf)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct usb_dev_state *ps = usb_get_intfdata(intf);
71762306a36Sopenharmony_ci	unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (!ps)
72062306a36Sopenharmony_ci		return;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* NOTE:  this relies on usbcore having canceled and completed
72362306a36Sopenharmony_ci	 * all pending I/O requests; 2.6 does that.
72462306a36Sopenharmony_ci	 */
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (likely(ifnum < 8*sizeof(ps->ifclaimed)))
72762306a36Sopenharmony_ci		clear_bit(ifnum, &ps->ifclaimed);
72862306a36Sopenharmony_ci	else
72962306a36Sopenharmony_ci		dev_warn(&intf->dev, "interface number %u out of range\n",
73062306a36Sopenharmony_ci			 ifnum);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* force async requests to complete */
73562306a36Sopenharmony_ci	destroy_async_on_interface(ps, ifnum);
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci/* We don't care about suspend/resume of claimed interfaces */
73962306a36Sopenharmony_cistatic int driver_suspend(struct usb_interface *intf, pm_message_t msg)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	return 0;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic int driver_resume(struct usb_interface *intf)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	return 0;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci#ifdef CONFIG_PM
75062306a36Sopenharmony_ci/* The following routines apply to the entire device, not interfaces */
75162306a36Sopenharmony_civoid usbfs_notify_suspend(struct usb_device *udev)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	/* We don't need to handle this */
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_civoid usbfs_notify_resume(struct usb_device *udev)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct usb_dev_state *ps;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/* Protect against simultaneous remove or release */
76162306a36Sopenharmony_ci	mutex_lock(&usbfs_mutex);
76262306a36Sopenharmony_ci	list_for_each_entry(ps, &udev->filelist, list) {
76362306a36Sopenharmony_ci		WRITE_ONCE(ps->not_yet_resumed, 0);
76462306a36Sopenharmony_ci		wake_up_all(&ps->wait_for_resume);
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	mutex_unlock(&usbfs_mutex);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci#endif
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistruct usb_driver usbfs_driver = {
77162306a36Sopenharmony_ci	.name =		"usbfs",
77262306a36Sopenharmony_ci	.probe =	driver_probe,
77362306a36Sopenharmony_ci	.disconnect =	driver_disconnect,
77462306a36Sopenharmony_ci	.suspend =	driver_suspend,
77562306a36Sopenharmony_ci	.resume =	driver_resume,
77662306a36Sopenharmony_ci	.supports_autosuspend = 1,
77762306a36Sopenharmony_ci};
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct usb_device *dev = ps->dev;
78262306a36Sopenharmony_ci	struct usb_interface *intf;
78362306a36Sopenharmony_ci	int err;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (ifnum >= 8*sizeof(ps->ifclaimed))
78662306a36Sopenharmony_ci		return -EINVAL;
78762306a36Sopenharmony_ci	/* already claimed */
78862306a36Sopenharmony_ci	if (test_bit(ifnum, &ps->ifclaimed))
78962306a36Sopenharmony_ci		return 0;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (ps->privileges_dropped &&
79262306a36Sopenharmony_ci			!test_bit(ifnum, &ps->interface_allowed_mask))
79362306a36Sopenharmony_ci		return -EACCES;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	intf = usb_ifnum_to_if(dev, ifnum);
79662306a36Sopenharmony_ci	if (!intf)
79762306a36Sopenharmony_ci		err = -ENOENT;
79862306a36Sopenharmony_ci	else {
79962306a36Sopenharmony_ci		unsigned int old_suppress;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		/* suppress uevents while claiming interface */
80262306a36Sopenharmony_ci		old_suppress = dev_get_uevent_suppress(&intf->dev);
80362306a36Sopenharmony_ci		dev_set_uevent_suppress(&intf->dev, 1);
80462306a36Sopenharmony_ci		err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
80562306a36Sopenharmony_ci		dev_set_uevent_suppress(&intf->dev, old_suppress);
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci	if (err == 0)
80862306a36Sopenharmony_ci		set_bit(ifnum, &ps->ifclaimed);
80962306a36Sopenharmony_ci	return err;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic int releaseintf(struct usb_dev_state *ps, unsigned int ifnum)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct usb_device *dev;
81562306a36Sopenharmony_ci	struct usb_interface *intf;
81662306a36Sopenharmony_ci	int err;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	err = -EINVAL;
81962306a36Sopenharmony_ci	if (ifnum >= 8*sizeof(ps->ifclaimed))
82062306a36Sopenharmony_ci		return err;
82162306a36Sopenharmony_ci	dev = ps->dev;
82262306a36Sopenharmony_ci	intf = usb_ifnum_to_if(dev, ifnum);
82362306a36Sopenharmony_ci	if (!intf)
82462306a36Sopenharmony_ci		err = -ENOENT;
82562306a36Sopenharmony_ci	else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) {
82662306a36Sopenharmony_ci		unsigned int old_suppress;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci		/* suppress uevents while releasing interface */
82962306a36Sopenharmony_ci		old_suppress = dev_get_uevent_suppress(&intf->dev);
83062306a36Sopenharmony_ci		dev_set_uevent_suppress(&intf->dev, 1);
83162306a36Sopenharmony_ci		usb_driver_release_interface(&usbfs_driver, intf);
83262306a36Sopenharmony_ci		dev_set_uevent_suppress(&intf->dev, old_suppress);
83362306a36Sopenharmony_ci		err = 0;
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci	return err;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic int checkintf(struct usb_dev_state *ps, unsigned int ifnum)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	if (ps->dev->state != USB_STATE_CONFIGURED)
84162306a36Sopenharmony_ci		return -EHOSTUNREACH;
84262306a36Sopenharmony_ci	if (ifnum >= 8*sizeof(ps->ifclaimed))
84362306a36Sopenharmony_ci		return -EINVAL;
84462306a36Sopenharmony_ci	if (test_bit(ifnum, &ps->ifclaimed))
84562306a36Sopenharmony_ci		return 0;
84662306a36Sopenharmony_ci	/* if not yet claimed, claim it for the driver */
84762306a36Sopenharmony_ci	dev_warn(&ps->dev->dev, "usbfs: process %d (%s) did not claim "
84862306a36Sopenharmony_ci		 "interface %u before use\n", task_pid_nr(current),
84962306a36Sopenharmony_ci		 current->comm, ifnum);
85062306a36Sopenharmony_ci	return claimintf(ps, ifnum);
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic int findintfep(struct usb_device *dev, unsigned int ep)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	unsigned int i, j, e;
85662306a36Sopenharmony_ci	struct usb_interface *intf;
85762306a36Sopenharmony_ci	struct usb_host_interface *alts;
85862306a36Sopenharmony_ci	struct usb_endpoint_descriptor *endpt;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (ep & ~(USB_DIR_IN|0xf))
86162306a36Sopenharmony_ci		return -EINVAL;
86262306a36Sopenharmony_ci	if (!dev->actconfig)
86362306a36Sopenharmony_ci		return -ESRCH;
86462306a36Sopenharmony_ci	for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
86562306a36Sopenharmony_ci		intf = dev->actconfig->interface[i];
86662306a36Sopenharmony_ci		for (j = 0; j < intf->num_altsetting; j++) {
86762306a36Sopenharmony_ci			alts = &intf->altsetting[j];
86862306a36Sopenharmony_ci			for (e = 0; e < alts->desc.bNumEndpoints; e++) {
86962306a36Sopenharmony_ci				endpt = &alts->endpoint[e].desc;
87062306a36Sopenharmony_ci				if (endpt->bEndpointAddress == ep)
87162306a36Sopenharmony_ci					return alts->desc.bInterfaceNumber;
87262306a36Sopenharmony_ci			}
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci	return -ENOENT;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int check_ctrlrecip(struct usb_dev_state *ps, unsigned int requesttype,
87962306a36Sopenharmony_ci			   unsigned int request, unsigned int index)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	int ret = 0;
88262306a36Sopenharmony_ci	struct usb_host_interface *alt_setting;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	if (ps->dev->state != USB_STATE_UNAUTHENTICATED
88562306a36Sopenharmony_ci	 && ps->dev->state != USB_STATE_ADDRESS
88662306a36Sopenharmony_ci	 && ps->dev->state != USB_STATE_CONFIGURED)
88762306a36Sopenharmony_ci		return -EHOSTUNREACH;
88862306a36Sopenharmony_ci	if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
88962306a36Sopenharmony_ci		return 0;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/*
89262306a36Sopenharmony_ci	 * check for the special corner case 'get_device_id' in the printer
89362306a36Sopenharmony_ci	 * class specification, which we always want to allow as it is used
89462306a36Sopenharmony_ci	 * to query things like ink level, etc.
89562306a36Sopenharmony_ci	 */
89662306a36Sopenharmony_ci	if (requesttype == 0xa1 && request == 0) {
89762306a36Sopenharmony_ci		alt_setting = usb_find_alt_setting(ps->dev->actconfig,
89862306a36Sopenharmony_ci						   index >> 8, index & 0xff);
89962306a36Sopenharmony_ci		if (alt_setting
90062306a36Sopenharmony_ci		 && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER)
90162306a36Sopenharmony_ci			return 0;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	index &= 0xff;
90562306a36Sopenharmony_ci	switch (requesttype & USB_RECIP_MASK) {
90662306a36Sopenharmony_ci	case USB_RECIP_ENDPOINT:
90762306a36Sopenharmony_ci		if ((index & ~USB_DIR_IN) == 0)
90862306a36Sopenharmony_ci			return 0;
90962306a36Sopenharmony_ci		ret = findintfep(ps->dev, index);
91062306a36Sopenharmony_ci		if (ret < 0) {
91162306a36Sopenharmony_ci			/*
91262306a36Sopenharmony_ci			 * Some not fully compliant Win apps seem to get
91362306a36Sopenharmony_ci			 * index wrong and have the endpoint number here
91462306a36Sopenharmony_ci			 * rather than the endpoint address (with the
91562306a36Sopenharmony_ci			 * correct direction). Win does let this through,
91662306a36Sopenharmony_ci			 * so we'll not reject it here but leave it to
91762306a36Sopenharmony_ci			 * the device to not break KVM. But we warn.
91862306a36Sopenharmony_ci			 */
91962306a36Sopenharmony_ci			ret = findintfep(ps->dev, index ^ 0x80);
92062306a36Sopenharmony_ci			if (ret >= 0)
92162306a36Sopenharmony_ci				dev_info(&ps->dev->dev,
92262306a36Sopenharmony_ci					"%s: process %i (%s) requesting ep %02x but needs %02x\n",
92362306a36Sopenharmony_ci					__func__, task_pid_nr(current),
92462306a36Sopenharmony_ci					current->comm, index, index ^ 0x80);
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci		if (ret >= 0)
92762306a36Sopenharmony_ci			ret = checkintf(ps, ret);
92862306a36Sopenharmony_ci		break;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	case USB_RECIP_INTERFACE:
93162306a36Sopenharmony_ci		ret = checkintf(ps, index);
93262306a36Sopenharmony_ci		break;
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci	return ret;
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev,
93862306a36Sopenharmony_ci						     unsigned char ep)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	if (ep & USB_ENDPOINT_DIR_MASK)
94162306a36Sopenharmony_ci		return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK];
94262306a36Sopenharmony_ci	else
94362306a36Sopenharmony_ci		return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK];
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic int parse_usbdevfs_streams(struct usb_dev_state *ps,
94762306a36Sopenharmony_ci				  struct usbdevfs_streams __user *streams,
94862306a36Sopenharmony_ci				  unsigned int *num_streams_ret,
94962306a36Sopenharmony_ci				  unsigned int *num_eps_ret,
95062306a36Sopenharmony_ci				  struct usb_host_endpoint ***eps_ret,
95162306a36Sopenharmony_ci				  struct usb_interface **intf_ret)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	unsigned int i, num_streams, num_eps;
95462306a36Sopenharmony_ci	struct usb_host_endpoint **eps;
95562306a36Sopenharmony_ci	struct usb_interface *intf = NULL;
95662306a36Sopenharmony_ci	unsigned char ep;
95762306a36Sopenharmony_ci	int ifnum, ret;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (get_user(num_streams, &streams->num_streams) ||
96062306a36Sopenharmony_ci	    get_user(num_eps, &streams->num_eps))
96162306a36Sopenharmony_ci		return -EFAULT;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (num_eps < 1 || num_eps > USB_MAXENDPOINTS)
96462306a36Sopenharmony_ci		return -EINVAL;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	/* The XHCI controller allows max 2 ^ 16 streams */
96762306a36Sopenharmony_ci	if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
96862306a36Sopenharmony_ci		return -EINVAL;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	eps = kmalloc_array(num_eps, sizeof(*eps), GFP_KERNEL);
97162306a36Sopenharmony_ci	if (!eps)
97262306a36Sopenharmony_ci		return -ENOMEM;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	for (i = 0; i < num_eps; i++) {
97562306a36Sopenharmony_ci		if (get_user(ep, &streams->eps[i])) {
97662306a36Sopenharmony_ci			ret = -EFAULT;
97762306a36Sopenharmony_ci			goto error;
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci		eps[i] = ep_to_host_endpoint(ps->dev, ep);
98062306a36Sopenharmony_ci		if (!eps[i]) {
98162306a36Sopenharmony_ci			ret = -EINVAL;
98262306a36Sopenharmony_ci			goto error;
98362306a36Sopenharmony_ci		}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		/* usb_alloc/free_streams operate on an usb_interface */
98662306a36Sopenharmony_ci		ifnum = findintfep(ps->dev, ep);
98762306a36Sopenharmony_ci		if (ifnum < 0) {
98862306a36Sopenharmony_ci			ret = ifnum;
98962306a36Sopenharmony_ci			goto error;
99062306a36Sopenharmony_ci		}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		if (i == 0) {
99362306a36Sopenharmony_ci			ret = checkintf(ps, ifnum);
99462306a36Sopenharmony_ci			if (ret < 0)
99562306a36Sopenharmony_ci				goto error;
99662306a36Sopenharmony_ci			intf = usb_ifnum_to_if(ps->dev, ifnum);
99762306a36Sopenharmony_ci		} else {
99862306a36Sopenharmony_ci			/* Verify all eps belong to the same interface */
99962306a36Sopenharmony_ci			if (ifnum != intf->altsetting->desc.bInterfaceNumber) {
100062306a36Sopenharmony_ci				ret = -EINVAL;
100162306a36Sopenharmony_ci				goto error;
100262306a36Sopenharmony_ci			}
100362306a36Sopenharmony_ci		}
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (num_streams_ret)
100762306a36Sopenharmony_ci		*num_streams_ret = num_streams;
100862306a36Sopenharmony_ci	*num_eps_ret = num_eps;
100962306a36Sopenharmony_ci	*eps_ret = eps;
101062306a36Sopenharmony_ci	*intf_ret = intf;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	return 0;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cierror:
101562306a36Sopenharmony_ci	kfree(eps);
101662306a36Sopenharmony_ci	return ret;
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic struct usb_device *usbdev_lookup_by_devt(dev_t devt)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct device *dev;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	dev = bus_find_device_by_devt(&usb_bus_type, devt);
102462306a36Sopenharmony_ci	if (!dev)
102562306a36Sopenharmony_ci		return NULL;
102662306a36Sopenharmony_ci	return to_usb_device(dev);
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci/*
103062306a36Sopenharmony_ci * file operations
103162306a36Sopenharmony_ci */
103262306a36Sopenharmony_cistatic int usbdev_open(struct inode *inode, struct file *file)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	struct usb_device *dev = NULL;
103562306a36Sopenharmony_ci	struct usb_dev_state *ps;
103662306a36Sopenharmony_ci	int ret;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	ret = -ENOMEM;
103962306a36Sopenharmony_ci	ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
104062306a36Sopenharmony_ci	if (!ps)
104162306a36Sopenharmony_ci		goto out_free_ps;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	ret = -ENODEV;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	/* usbdev device-node */
104662306a36Sopenharmony_ci	if (imajor(inode) == USB_DEVICE_MAJOR)
104762306a36Sopenharmony_ci		dev = usbdev_lookup_by_devt(inode->i_rdev);
104862306a36Sopenharmony_ci	if (!dev)
104962306a36Sopenharmony_ci		goto out_free_ps;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	usb_lock_device(dev);
105262306a36Sopenharmony_ci	if (dev->state == USB_STATE_NOTATTACHED)
105362306a36Sopenharmony_ci		goto out_unlock_device;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	ret = usb_autoresume_device(dev);
105662306a36Sopenharmony_ci	if (ret)
105762306a36Sopenharmony_ci		goto out_unlock_device;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	ps->dev = dev;
106062306a36Sopenharmony_ci	ps->file = file;
106162306a36Sopenharmony_ci	ps->interface_allowed_mask = 0xFFFFFFFF; /* 32 bits */
106262306a36Sopenharmony_ci	spin_lock_init(&ps->lock);
106362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ps->list);
106462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ps->async_pending);
106562306a36Sopenharmony_ci	INIT_LIST_HEAD(&ps->async_completed);
106662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ps->memory_list);
106762306a36Sopenharmony_ci	init_waitqueue_head(&ps->wait);
106862306a36Sopenharmony_ci	init_waitqueue_head(&ps->wait_for_resume);
106962306a36Sopenharmony_ci	ps->disc_pid = get_pid(task_pid(current));
107062306a36Sopenharmony_ci	ps->cred = get_current_cred();
107162306a36Sopenharmony_ci	smp_wmb();
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* Can't race with resume; the device is already active */
107462306a36Sopenharmony_ci	list_add_tail(&ps->list, &dev->filelist);
107562306a36Sopenharmony_ci	file->private_data = ps;
107662306a36Sopenharmony_ci	usb_unlock_device(dev);
107762306a36Sopenharmony_ci	snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current),
107862306a36Sopenharmony_ci			current->comm);
107962306a36Sopenharmony_ci	return ret;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci out_unlock_device:
108262306a36Sopenharmony_ci	usb_unlock_device(dev);
108362306a36Sopenharmony_ci	usb_put_dev(dev);
108462306a36Sopenharmony_ci out_free_ps:
108562306a36Sopenharmony_ci	kfree(ps);
108662306a36Sopenharmony_ci	return ret;
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic int usbdev_release(struct inode *inode, struct file *file)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	struct usb_dev_state *ps = file->private_data;
109262306a36Sopenharmony_ci	struct usb_device *dev = ps->dev;
109362306a36Sopenharmony_ci	unsigned int ifnum;
109462306a36Sopenharmony_ci	struct async *as;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	usb_lock_device(dev);
109762306a36Sopenharmony_ci	usb_hub_release_all_ports(dev, ps);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	/* Protect against simultaneous resume */
110062306a36Sopenharmony_ci	mutex_lock(&usbfs_mutex);
110162306a36Sopenharmony_ci	list_del_init(&ps->list);
110262306a36Sopenharmony_ci	mutex_unlock(&usbfs_mutex);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
110562306a36Sopenharmony_ci			ifnum++) {
110662306a36Sopenharmony_ci		if (test_bit(ifnum, &ps->ifclaimed))
110762306a36Sopenharmony_ci			releaseintf(ps, ifnum);
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci	destroy_all_async(ps);
111062306a36Sopenharmony_ci	if (!ps->suspend_allowed)
111162306a36Sopenharmony_ci		usb_autosuspend_device(dev);
111262306a36Sopenharmony_ci	usb_unlock_device(dev);
111362306a36Sopenharmony_ci	usb_put_dev(dev);
111462306a36Sopenharmony_ci	put_pid(ps->disc_pid);
111562306a36Sopenharmony_ci	put_cred(ps->cred);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	as = async_getcompleted(ps);
111862306a36Sopenharmony_ci	while (as) {
111962306a36Sopenharmony_ci		free_async(as);
112062306a36Sopenharmony_ci		as = async_getcompleted(ps);
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	kfree(ps);
112462306a36Sopenharmony_ci	return 0;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_cistatic void usbfs_blocking_completion(struct urb *urb)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	complete((struct completion *) urb->context);
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci/*
113362306a36Sopenharmony_ci * Much like usb_start_wait_urb, but returns status separately from
113462306a36Sopenharmony_ci * actual_length and uses a killable wait.
113562306a36Sopenharmony_ci */
113662306a36Sopenharmony_cistatic int usbfs_start_wait_urb(struct urb *urb, int timeout,
113762306a36Sopenharmony_ci		unsigned int *actlen)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(ctx);
114062306a36Sopenharmony_ci	unsigned long expire;
114162306a36Sopenharmony_ci	int rc;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	urb->context = &ctx;
114462306a36Sopenharmony_ci	urb->complete = usbfs_blocking_completion;
114562306a36Sopenharmony_ci	*actlen = 0;
114662306a36Sopenharmony_ci	rc = usb_submit_urb(urb, GFP_KERNEL);
114762306a36Sopenharmony_ci	if (unlikely(rc))
114862306a36Sopenharmony_ci		return rc;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	expire = (timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT);
115162306a36Sopenharmony_ci	rc = wait_for_completion_killable_timeout(&ctx, expire);
115262306a36Sopenharmony_ci	if (rc <= 0) {
115362306a36Sopenharmony_ci		usb_kill_urb(urb);
115462306a36Sopenharmony_ci		*actlen = urb->actual_length;
115562306a36Sopenharmony_ci		if (urb->status != -ENOENT)
115662306a36Sopenharmony_ci			;	/* Completed before it was killed */
115762306a36Sopenharmony_ci		else if (rc < 0)
115862306a36Sopenharmony_ci			return -EINTR;
115962306a36Sopenharmony_ci		else
116062306a36Sopenharmony_ci			return -ETIMEDOUT;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci	*actlen = urb->actual_length;
116362306a36Sopenharmony_ci	return urb->status;
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int do_proc_control(struct usb_dev_state *ps,
116762306a36Sopenharmony_ci		struct usbdevfs_ctrltransfer *ctrl)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	struct usb_device *dev = ps->dev;
117062306a36Sopenharmony_ci	unsigned int tmo;
117162306a36Sopenharmony_ci	unsigned char *tbuf;
117262306a36Sopenharmony_ci	unsigned int wLength, actlen;
117362306a36Sopenharmony_ci	int i, pipe, ret;
117462306a36Sopenharmony_ci	struct urb *urb = NULL;
117562306a36Sopenharmony_ci	struct usb_ctrlrequest *dr = NULL;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ret = check_ctrlrecip(ps, ctrl->bRequestType, ctrl->bRequest,
117862306a36Sopenharmony_ci			      ctrl->wIndex);
117962306a36Sopenharmony_ci	if (ret)
118062306a36Sopenharmony_ci		return ret;
118162306a36Sopenharmony_ci	wLength = ctrl->wLength;	/* To suppress 64k PAGE_SIZE warning */
118262306a36Sopenharmony_ci	if (wLength > PAGE_SIZE)
118362306a36Sopenharmony_ci		return -EINVAL;
118462306a36Sopenharmony_ci	ret = usbfs_increase_memory_usage(PAGE_SIZE + sizeof(struct urb) +
118562306a36Sopenharmony_ci			sizeof(struct usb_ctrlrequest));
118662306a36Sopenharmony_ci	if (ret)
118762306a36Sopenharmony_ci		return ret;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	ret = -ENOMEM;
119062306a36Sopenharmony_ci	tbuf = (unsigned char *)__get_free_page(GFP_KERNEL);
119162306a36Sopenharmony_ci	if (!tbuf)
119262306a36Sopenharmony_ci		goto done;
119362306a36Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_NOIO);
119462306a36Sopenharmony_ci	if (!urb)
119562306a36Sopenharmony_ci		goto done;
119662306a36Sopenharmony_ci	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
119762306a36Sopenharmony_ci	if (!dr)
119862306a36Sopenharmony_ci		goto done;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	dr->bRequestType = ctrl->bRequestType;
120162306a36Sopenharmony_ci	dr->bRequest = ctrl->bRequest;
120262306a36Sopenharmony_ci	dr->wValue = cpu_to_le16(ctrl->wValue);
120362306a36Sopenharmony_ci	dr->wIndex = cpu_to_le16(ctrl->wIndex);
120462306a36Sopenharmony_ci	dr->wLength = cpu_to_le16(ctrl->wLength);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	tmo = ctrl->timeout;
120762306a36Sopenharmony_ci	snoop(&dev->dev, "control urb: bRequestType=%02x "
120862306a36Sopenharmony_ci		"bRequest=%02x wValue=%04x "
120962306a36Sopenharmony_ci		"wIndex=%04x wLength=%04x\n",
121062306a36Sopenharmony_ci		ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
121162306a36Sopenharmony_ci		ctrl->wIndex, ctrl->wLength);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	if ((ctrl->bRequestType & USB_DIR_IN) && wLength) {
121462306a36Sopenharmony_ci		pipe = usb_rcvctrlpipe(dev, 0);
121562306a36Sopenharmony_ci		usb_fill_control_urb(urb, dev, pipe, (unsigned char *) dr, tbuf,
121662306a36Sopenharmony_ci				wLength, NULL, NULL);
121762306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, wLength, tmo, SUBMIT, NULL, 0);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		usb_unlock_device(dev);
122062306a36Sopenharmony_ci		i = usbfs_start_wait_urb(urb, tmo, &actlen);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		/* Linger a bit, prior to the next control message. */
122362306a36Sopenharmony_ci		if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
122462306a36Sopenharmony_ci			msleep(200);
122562306a36Sopenharmony_ci		usb_lock_device(dev);
122662306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, tbuf, actlen);
122762306a36Sopenharmony_ci		if (!i && actlen) {
122862306a36Sopenharmony_ci			if (copy_to_user(ctrl->data, tbuf, actlen)) {
122962306a36Sopenharmony_ci				ret = -EFAULT;
123062306a36Sopenharmony_ci				goto done;
123162306a36Sopenharmony_ci			}
123262306a36Sopenharmony_ci		}
123362306a36Sopenharmony_ci	} else {
123462306a36Sopenharmony_ci		if (wLength) {
123562306a36Sopenharmony_ci			if (copy_from_user(tbuf, ctrl->data, wLength)) {
123662306a36Sopenharmony_ci				ret = -EFAULT;
123762306a36Sopenharmony_ci				goto done;
123862306a36Sopenharmony_ci			}
123962306a36Sopenharmony_ci		}
124062306a36Sopenharmony_ci		pipe = usb_sndctrlpipe(dev, 0);
124162306a36Sopenharmony_ci		usb_fill_control_urb(urb, dev, pipe, (unsigned char *) dr, tbuf,
124262306a36Sopenharmony_ci				wLength, NULL, NULL);
124362306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, wLength, tmo, SUBMIT, tbuf, wLength);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		usb_unlock_device(dev);
124662306a36Sopenharmony_ci		i = usbfs_start_wait_urb(urb, tmo, &actlen);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci		/* Linger a bit, prior to the next control message. */
124962306a36Sopenharmony_ci		if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
125062306a36Sopenharmony_ci			msleep(200);
125162306a36Sopenharmony_ci		usb_lock_device(dev);
125262306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, NULL, 0);
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci	if (i < 0 && i != -EPIPE) {
125562306a36Sopenharmony_ci		dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL "
125662306a36Sopenharmony_ci			   "failed cmd %s rqt %u rq %u len %u ret %d\n",
125762306a36Sopenharmony_ci			   current->comm, ctrl->bRequestType, ctrl->bRequest,
125862306a36Sopenharmony_ci			   ctrl->wLength, i);
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci	ret = (i < 0 ? i : actlen);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci done:
126362306a36Sopenharmony_ci	kfree(dr);
126462306a36Sopenharmony_ci	usb_free_urb(urb);
126562306a36Sopenharmony_ci	free_page((unsigned long) tbuf);
126662306a36Sopenharmony_ci	usbfs_decrease_memory_usage(PAGE_SIZE + sizeof(struct urb) +
126762306a36Sopenharmony_ci			sizeof(struct usb_ctrlrequest));
126862306a36Sopenharmony_ci	return ret;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_cistatic int proc_control(struct usb_dev_state *ps, void __user *arg)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	struct usbdevfs_ctrltransfer ctrl;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
127662306a36Sopenharmony_ci		return -EFAULT;
127762306a36Sopenharmony_ci	return do_proc_control(ps, &ctrl);
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic int do_proc_bulk(struct usb_dev_state *ps,
128162306a36Sopenharmony_ci		struct usbdevfs_bulktransfer *bulk)
128262306a36Sopenharmony_ci{
128362306a36Sopenharmony_ci	struct usb_device *dev = ps->dev;
128462306a36Sopenharmony_ci	unsigned int tmo, len1, len2, pipe;
128562306a36Sopenharmony_ci	unsigned char *tbuf;
128662306a36Sopenharmony_ci	int i, ret;
128762306a36Sopenharmony_ci	struct urb *urb = NULL;
128862306a36Sopenharmony_ci	struct usb_host_endpoint *ep;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	ret = findintfep(ps->dev, bulk->ep);
129162306a36Sopenharmony_ci	if (ret < 0)
129262306a36Sopenharmony_ci		return ret;
129362306a36Sopenharmony_ci	ret = checkintf(ps, ret);
129462306a36Sopenharmony_ci	if (ret)
129562306a36Sopenharmony_ci		return ret;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	len1 = bulk->len;
129862306a36Sopenharmony_ci	if (len1 < 0 || len1 >= (INT_MAX - sizeof(struct urb)))
129962306a36Sopenharmony_ci		return -EINVAL;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (bulk->ep & USB_DIR_IN)
130262306a36Sopenharmony_ci		pipe = usb_rcvbulkpipe(dev, bulk->ep & 0x7f);
130362306a36Sopenharmony_ci	else
130462306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(dev, bulk->ep & 0x7f);
130562306a36Sopenharmony_ci	ep = usb_pipe_endpoint(dev, pipe);
130662306a36Sopenharmony_ci	if (!ep || !usb_endpoint_maxp(&ep->desc))
130762306a36Sopenharmony_ci		return -EINVAL;
130862306a36Sopenharmony_ci	ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
130962306a36Sopenharmony_ci	if (ret)
131062306a36Sopenharmony_ci		return ret;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	/*
131362306a36Sopenharmony_ci	 * len1 can be almost arbitrarily large.  Don't WARN if it's
131462306a36Sopenharmony_ci	 * too big, just fail the request.
131562306a36Sopenharmony_ci	 */
131662306a36Sopenharmony_ci	ret = -ENOMEM;
131762306a36Sopenharmony_ci	tbuf = kmalloc(len1, GFP_KERNEL | __GFP_NOWARN);
131862306a36Sopenharmony_ci	if (!tbuf)
131962306a36Sopenharmony_ci		goto done;
132062306a36Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);
132162306a36Sopenharmony_ci	if (!urb)
132262306a36Sopenharmony_ci		goto done;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
132562306a36Sopenharmony_ci			USB_ENDPOINT_XFER_INT) {
132662306a36Sopenharmony_ci		pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
132762306a36Sopenharmony_ci		usb_fill_int_urb(urb, dev, pipe, tbuf, len1,
132862306a36Sopenharmony_ci				NULL, NULL, ep->desc.bInterval);
132962306a36Sopenharmony_ci	} else {
133062306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, dev, pipe, tbuf, len1, NULL, NULL);
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	tmo = bulk->timeout;
133462306a36Sopenharmony_ci	if (bulk->ep & 0x80) {
133562306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci		usb_unlock_device(dev);
133862306a36Sopenharmony_ci		i = usbfs_start_wait_urb(urb, tmo, &len2);
133962306a36Sopenharmony_ci		usb_lock_device(dev);
134062306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci		if (!i && len2) {
134362306a36Sopenharmony_ci			if (copy_to_user(bulk->data, tbuf, len2)) {
134462306a36Sopenharmony_ci				ret = -EFAULT;
134562306a36Sopenharmony_ci				goto done;
134662306a36Sopenharmony_ci			}
134762306a36Sopenharmony_ci		}
134862306a36Sopenharmony_ci	} else {
134962306a36Sopenharmony_ci		if (len1) {
135062306a36Sopenharmony_ci			if (copy_from_user(tbuf, bulk->data, len1)) {
135162306a36Sopenharmony_ci				ret = -EFAULT;
135262306a36Sopenharmony_ci				goto done;
135362306a36Sopenharmony_ci			}
135462306a36Sopenharmony_ci		}
135562306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		usb_unlock_device(dev);
135862306a36Sopenharmony_ci		i = usbfs_start_wait_urb(urb, tmo, &len2);
135962306a36Sopenharmony_ci		usb_lock_device(dev);
136062306a36Sopenharmony_ci		snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0);
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci	ret = (i < 0 ? i : len2);
136362306a36Sopenharmony_ci done:
136462306a36Sopenharmony_ci	usb_free_urb(urb);
136562306a36Sopenharmony_ci	kfree(tbuf);
136662306a36Sopenharmony_ci	usbfs_decrease_memory_usage(len1 + sizeof(struct urb));
136762306a36Sopenharmony_ci	return ret;
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_cistatic int proc_bulk(struct usb_dev_state *ps, void __user *arg)
137162306a36Sopenharmony_ci{
137262306a36Sopenharmony_ci	struct usbdevfs_bulktransfer bulk;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (copy_from_user(&bulk, arg, sizeof(bulk)))
137562306a36Sopenharmony_ci		return -EFAULT;
137662306a36Sopenharmony_ci	return do_proc_bulk(ps, &bulk);
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic void check_reset_of_active_ep(struct usb_device *udev,
138062306a36Sopenharmony_ci		unsigned int epnum, char *ioctl_name)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	struct usb_host_endpoint **eps;
138362306a36Sopenharmony_ci	struct usb_host_endpoint *ep;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out;
138662306a36Sopenharmony_ci	ep = eps[epnum & 0x0f];
138762306a36Sopenharmony_ci	if (ep && !list_empty(&ep->urb_list))
138862306a36Sopenharmony_ci		dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n",
138962306a36Sopenharmony_ci				task_pid_nr(current), current->comm,
139062306a36Sopenharmony_ci				ioctl_name, epnum);
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_cistatic int proc_resetep(struct usb_dev_state *ps, void __user *arg)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	unsigned int ep;
139662306a36Sopenharmony_ci	int ret;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	if (get_user(ep, (unsigned int __user *)arg))
139962306a36Sopenharmony_ci		return -EFAULT;
140062306a36Sopenharmony_ci	ret = findintfep(ps->dev, ep);
140162306a36Sopenharmony_ci	if (ret < 0)
140262306a36Sopenharmony_ci		return ret;
140362306a36Sopenharmony_ci	ret = checkintf(ps, ret);
140462306a36Sopenharmony_ci	if (ret)
140562306a36Sopenharmony_ci		return ret;
140662306a36Sopenharmony_ci	check_reset_of_active_ep(ps->dev, ep, "RESETEP");
140762306a36Sopenharmony_ci	usb_reset_endpoint(ps->dev, ep);
140862306a36Sopenharmony_ci	return 0;
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_cistatic int proc_clearhalt(struct usb_dev_state *ps, void __user *arg)
141262306a36Sopenharmony_ci{
141362306a36Sopenharmony_ci	unsigned int ep;
141462306a36Sopenharmony_ci	int pipe;
141562306a36Sopenharmony_ci	int ret;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (get_user(ep, (unsigned int __user *)arg))
141862306a36Sopenharmony_ci		return -EFAULT;
141962306a36Sopenharmony_ci	ret = findintfep(ps->dev, ep);
142062306a36Sopenharmony_ci	if (ret < 0)
142162306a36Sopenharmony_ci		return ret;
142262306a36Sopenharmony_ci	ret = checkintf(ps, ret);
142362306a36Sopenharmony_ci	if (ret)
142462306a36Sopenharmony_ci		return ret;
142562306a36Sopenharmony_ci	check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT");
142662306a36Sopenharmony_ci	if (ep & USB_DIR_IN)
142762306a36Sopenharmony_ci		pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
142862306a36Sopenharmony_ci	else
142962306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	return usb_clear_halt(ps->dev, pipe);
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic int proc_getdriver(struct usb_dev_state *ps, void __user *arg)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	struct usbdevfs_getdriver gd;
143762306a36Sopenharmony_ci	struct usb_interface *intf;
143862306a36Sopenharmony_ci	int ret;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	if (copy_from_user(&gd, arg, sizeof(gd)))
144162306a36Sopenharmony_ci		return -EFAULT;
144262306a36Sopenharmony_ci	intf = usb_ifnum_to_if(ps->dev, gd.interface);
144362306a36Sopenharmony_ci	if (!intf || !intf->dev.driver)
144462306a36Sopenharmony_ci		ret = -ENODATA;
144562306a36Sopenharmony_ci	else {
144662306a36Sopenharmony_ci		strscpy(gd.driver, intf->dev.driver->name,
144762306a36Sopenharmony_ci				sizeof(gd.driver));
144862306a36Sopenharmony_ci		ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci	return ret;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct usbdevfs_connectinfo ci;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	memset(&ci, 0, sizeof(ci));
145862306a36Sopenharmony_ci	ci.devnum = ps->dev->devnum;
145962306a36Sopenharmony_ci	ci.slow = ps->dev->speed == USB_SPEED_LOW;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	if (copy_to_user(arg, &ci, sizeof(ci)))
146262306a36Sopenharmony_ci		return -EFAULT;
146362306a36Sopenharmony_ci	return 0;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic int proc_conninfo_ex(struct usb_dev_state *ps,
146762306a36Sopenharmony_ci			    void __user *arg, size_t size)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct usbdevfs_conninfo_ex ci;
147062306a36Sopenharmony_ci	struct usb_device *udev = ps->dev;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (size < sizeof(ci.size))
147362306a36Sopenharmony_ci		return -EINVAL;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	memset(&ci, 0, sizeof(ci));
147662306a36Sopenharmony_ci	ci.size = sizeof(ci);
147762306a36Sopenharmony_ci	ci.busnum = udev->bus->busnum;
147862306a36Sopenharmony_ci	ci.devnum = udev->devnum;
147962306a36Sopenharmony_ci	ci.speed = udev->speed;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	while (udev && udev->portnum != 0) {
148262306a36Sopenharmony_ci		if (++ci.num_ports <= ARRAY_SIZE(ci.ports))
148362306a36Sopenharmony_ci			ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports] =
148462306a36Sopenharmony_ci					udev->portnum;
148562306a36Sopenharmony_ci		udev = udev->parent;
148662306a36Sopenharmony_ci	}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (ci.num_ports < ARRAY_SIZE(ci.ports))
148962306a36Sopenharmony_ci		memmove(&ci.ports[0],
149062306a36Sopenharmony_ci			&ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports],
149162306a36Sopenharmony_ci			ci.num_ports);
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (copy_to_user(arg, &ci, min(sizeof(ci), size)))
149462306a36Sopenharmony_ci		return -EFAULT;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	return 0;
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_cistatic int proc_resetdevice(struct usb_dev_state *ps)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	struct usb_host_config *actconfig = ps->dev->actconfig;
150262306a36Sopenharmony_ci	struct usb_interface *interface;
150362306a36Sopenharmony_ci	int i, number;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	/* Don't allow a device reset if the process has dropped the
150662306a36Sopenharmony_ci	 * privilege to do such things and any of the interfaces are
150762306a36Sopenharmony_ci	 * currently claimed.
150862306a36Sopenharmony_ci	 */
150962306a36Sopenharmony_ci	if (ps->privileges_dropped && actconfig) {
151062306a36Sopenharmony_ci		for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
151162306a36Sopenharmony_ci			interface = actconfig->interface[i];
151262306a36Sopenharmony_ci			number = interface->cur_altsetting->desc.bInterfaceNumber;
151362306a36Sopenharmony_ci			if (usb_interface_claimed(interface) &&
151462306a36Sopenharmony_ci					!test_bit(number, &ps->ifclaimed)) {
151562306a36Sopenharmony_ci				dev_warn(&ps->dev->dev,
151662306a36Sopenharmony_ci					"usbfs: interface %d claimed by %s while '%s' resets device\n",
151762306a36Sopenharmony_ci					number,	interface->dev.driver->name, current->comm);
151862306a36Sopenharmony_ci				return -EACCES;
151962306a36Sopenharmony_ci			}
152062306a36Sopenharmony_ci		}
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	return usb_reset_device(ps->dev);
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic int proc_setintf(struct usb_dev_state *ps, void __user *arg)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	struct usbdevfs_setinterface setintf;
152962306a36Sopenharmony_ci	int ret;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	if (copy_from_user(&setintf, arg, sizeof(setintf)))
153262306a36Sopenharmony_ci		return -EFAULT;
153362306a36Sopenharmony_ci	ret = checkintf(ps, setintf.interface);
153462306a36Sopenharmony_ci	if (ret)
153562306a36Sopenharmony_ci		return ret;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	destroy_async_on_interface(ps, setintf.interface);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	return usb_set_interface(ps->dev, setintf.interface,
154062306a36Sopenharmony_ci			setintf.altsetting);
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_cistatic int proc_setconfig(struct usb_dev_state *ps, void __user *arg)
154462306a36Sopenharmony_ci{
154562306a36Sopenharmony_ci	int u;
154662306a36Sopenharmony_ci	int status = 0;
154762306a36Sopenharmony_ci	struct usb_host_config *actconfig;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (get_user(u, (int __user *)arg))
155062306a36Sopenharmony_ci		return -EFAULT;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	actconfig = ps->dev->actconfig;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	/* Don't touch the device if any interfaces are claimed.
155562306a36Sopenharmony_ci	 * It could interfere with other drivers' operations, and if
155662306a36Sopenharmony_ci	 * an interface is claimed by usbfs it could easily deadlock.
155762306a36Sopenharmony_ci	 */
155862306a36Sopenharmony_ci	if (actconfig) {
155962306a36Sopenharmony_ci		int i;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci		for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
156262306a36Sopenharmony_ci			if (usb_interface_claimed(actconfig->interface[i])) {
156362306a36Sopenharmony_ci				dev_warn(&ps->dev->dev,
156462306a36Sopenharmony_ci					"usbfs: interface %d claimed by %s "
156562306a36Sopenharmony_ci					"while '%s' sets config #%d\n",
156662306a36Sopenharmony_ci					actconfig->interface[i]
156762306a36Sopenharmony_ci						->cur_altsetting
156862306a36Sopenharmony_ci						->desc.bInterfaceNumber,
156962306a36Sopenharmony_ci					actconfig->interface[i]
157062306a36Sopenharmony_ci						->dev.driver->name,
157162306a36Sopenharmony_ci					current->comm, u);
157262306a36Sopenharmony_ci				status = -EBUSY;
157362306a36Sopenharmony_ci				break;
157462306a36Sopenharmony_ci			}
157562306a36Sopenharmony_ci		}
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	/* SET_CONFIGURATION is often abused as a "cheap" driver reset,
157962306a36Sopenharmony_ci	 * so avoid usb_set_configuration()'s kick to sysfs
158062306a36Sopenharmony_ci	 */
158162306a36Sopenharmony_ci	if (status == 0) {
158262306a36Sopenharmony_ci		if (actconfig && actconfig->desc.bConfigurationValue == u)
158362306a36Sopenharmony_ci			status = usb_reset_configuration(ps->dev);
158462306a36Sopenharmony_ci		else
158562306a36Sopenharmony_ci			status = usb_set_configuration(ps->dev, u);
158662306a36Sopenharmony_ci	}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	return status;
158962306a36Sopenharmony_ci}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_cistatic struct usb_memory *
159262306a36Sopenharmony_cifind_memory_area(struct usb_dev_state *ps, const struct usbdevfs_urb *uurb)
159362306a36Sopenharmony_ci{
159462306a36Sopenharmony_ci	struct usb_memory *usbm = NULL, *iter;
159562306a36Sopenharmony_ci	unsigned long flags;
159662306a36Sopenharmony_ci	unsigned long uurb_start = (unsigned long)uurb->buffer;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
159962306a36Sopenharmony_ci	list_for_each_entry(iter, &ps->memory_list, memlist) {
160062306a36Sopenharmony_ci		if (uurb_start >= iter->vm_start &&
160162306a36Sopenharmony_ci				uurb_start < iter->vm_start + iter->size) {
160262306a36Sopenharmony_ci			if (uurb->buffer_length > iter->vm_start + iter->size -
160362306a36Sopenharmony_ci					uurb_start) {
160462306a36Sopenharmony_ci				usbm = ERR_PTR(-EINVAL);
160562306a36Sopenharmony_ci			} else {
160662306a36Sopenharmony_ci				usbm = iter;
160762306a36Sopenharmony_ci				usbm->urb_use_count++;
160862306a36Sopenharmony_ci			}
160962306a36Sopenharmony_ci			break;
161062306a36Sopenharmony_ci		}
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
161362306a36Sopenharmony_ci	return usbm;
161462306a36Sopenharmony_ci}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_cistatic int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,
161762306a36Sopenharmony_ci			struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
161862306a36Sopenharmony_ci			void __user *arg, sigval_t userurb_sigval)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct usbdevfs_iso_packet_desc *isopkt = NULL;
162162306a36Sopenharmony_ci	struct usb_host_endpoint *ep;
162262306a36Sopenharmony_ci	struct async *as = NULL;
162362306a36Sopenharmony_ci	struct usb_ctrlrequest *dr = NULL;
162462306a36Sopenharmony_ci	unsigned int u, totlen, isofrmlen;
162562306a36Sopenharmony_ci	int i, ret, num_sgs = 0, ifnum = -1;
162662306a36Sopenharmony_ci	int number_of_packets = 0;
162762306a36Sopenharmony_ci	unsigned int stream_id = 0;
162862306a36Sopenharmony_ci	void *buf;
162962306a36Sopenharmony_ci	bool is_in;
163062306a36Sopenharmony_ci	bool allow_short = false;
163162306a36Sopenharmony_ci	bool allow_zero = false;
163262306a36Sopenharmony_ci	unsigned long mask =	USBDEVFS_URB_SHORT_NOT_OK |
163362306a36Sopenharmony_ci				USBDEVFS_URB_BULK_CONTINUATION |
163462306a36Sopenharmony_ci				USBDEVFS_URB_NO_FSBR |
163562306a36Sopenharmony_ci				USBDEVFS_URB_ZERO_PACKET |
163662306a36Sopenharmony_ci				USBDEVFS_URB_NO_INTERRUPT;
163762306a36Sopenharmony_ci	/* USBDEVFS_URB_ISO_ASAP is a special case */
163862306a36Sopenharmony_ci	if (uurb->type == USBDEVFS_URB_TYPE_ISO)
163962306a36Sopenharmony_ci		mask |= USBDEVFS_URB_ISO_ASAP;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	if (uurb->flags & ~mask)
164262306a36Sopenharmony_ci			return -EINVAL;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	if ((unsigned int)uurb->buffer_length >= USBFS_XFER_MAX)
164562306a36Sopenharmony_ci		return -EINVAL;
164662306a36Sopenharmony_ci	if (uurb->buffer_length > 0 && !uurb->buffer)
164762306a36Sopenharmony_ci		return -EINVAL;
164862306a36Sopenharmony_ci	if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
164962306a36Sopenharmony_ci	    (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
165062306a36Sopenharmony_ci		ifnum = findintfep(ps->dev, uurb->endpoint);
165162306a36Sopenharmony_ci		if (ifnum < 0)
165262306a36Sopenharmony_ci			return ifnum;
165362306a36Sopenharmony_ci		ret = checkintf(ps, ifnum);
165462306a36Sopenharmony_ci		if (ret)
165562306a36Sopenharmony_ci			return ret;
165662306a36Sopenharmony_ci	}
165762306a36Sopenharmony_ci	ep = ep_to_host_endpoint(ps->dev, uurb->endpoint);
165862306a36Sopenharmony_ci	if (!ep)
165962306a36Sopenharmony_ci		return -ENOENT;
166062306a36Sopenharmony_ci	is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	u = 0;
166362306a36Sopenharmony_ci	switch (uurb->type) {
166462306a36Sopenharmony_ci	case USBDEVFS_URB_TYPE_CONTROL:
166562306a36Sopenharmony_ci		if (!usb_endpoint_xfer_control(&ep->desc))
166662306a36Sopenharmony_ci			return -EINVAL;
166762306a36Sopenharmony_ci		/* min 8 byte setup packet */
166862306a36Sopenharmony_ci		if (uurb->buffer_length < 8)
166962306a36Sopenharmony_ci			return -EINVAL;
167062306a36Sopenharmony_ci		dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
167162306a36Sopenharmony_ci		if (!dr)
167262306a36Sopenharmony_ci			return -ENOMEM;
167362306a36Sopenharmony_ci		if (copy_from_user(dr, uurb->buffer, 8)) {
167462306a36Sopenharmony_ci			ret = -EFAULT;
167562306a36Sopenharmony_ci			goto error;
167662306a36Sopenharmony_ci		}
167762306a36Sopenharmony_ci		if (uurb->buffer_length < (le16_to_cpu(dr->wLength) + 8)) {
167862306a36Sopenharmony_ci			ret = -EINVAL;
167962306a36Sopenharmony_ci			goto error;
168062306a36Sopenharmony_ci		}
168162306a36Sopenharmony_ci		ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
168262306a36Sopenharmony_ci				      le16_to_cpu(dr->wIndex));
168362306a36Sopenharmony_ci		if (ret)
168462306a36Sopenharmony_ci			goto error;
168562306a36Sopenharmony_ci		uurb->buffer_length = le16_to_cpu(dr->wLength);
168662306a36Sopenharmony_ci		uurb->buffer += 8;
168762306a36Sopenharmony_ci		if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
168862306a36Sopenharmony_ci			is_in = true;
168962306a36Sopenharmony_ci			uurb->endpoint |= USB_DIR_IN;
169062306a36Sopenharmony_ci		} else {
169162306a36Sopenharmony_ci			is_in = false;
169262306a36Sopenharmony_ci			uurb->endpoint &= ~USB_DIR_IN;
169362306a36Sopenharmony_ci		}
169462306a36Sopenharmony_ci		if (is_in)
169562306a36Sopenharmony_ci			allow_short = true;
169662306a36Sopenharmony_ci		snoop(&ps->dev->dev, "control urb: bRequestType=%02x "
169762306a36Sopenharmony_ci			"bRequest=%02x wValue=%04x "
169862306a36Sopenharmony_ci			"wIndex=%04x wLength=%04x\n",
169962306a36Sopenharmony_ci			dr->bRequestType, dr->bRequest,
170062306a36Sopenharmony_ci			__le16_to_cpu(dr->wValue),
170162306a36Sopenharmony_ci			__le16_to_cpu(dr->wIndex),
170262306a36Sopenharmony_ci			__le16_to_cpu(dr->wLength));
170362306a36Sopenharmony_ci		u = sizeof(struct usb_ctrlrequest);
170462306a36Sopenharmony_ci		break;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	case USBDEVFS_URB_TYPE_BULK:
170762306a36Sopenharmony_ci		if (!is_in)
170862306a36Sopenharmony_ci			allow_zero = true;
170962306a36Sopenharmony_ci		else
171062306a36Sopenharmony_ci			allow_short = true;
171162306a36Sopenharmony_ci		switch (usb_endpoint_type(&ep->desc)) {
171262306a36Sopenharmony_ci		case USB_ENDPOINT_XFER_CONTROL:
171362306a36Sopenharmony_ci		case USB_ENDPOINT_XFER_ISOC:
171462306a36Sopenharmony_ci			return -EINVAL;
171562306a36Sopenharmony_ci		case USB_ENDPOINT_XFER_INT:
171662306a36Sopenharmony_ci			/* allow single-shot interrupt transfers */
171762306a36Sopenharmony_ci			uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
171862306a36Sopenharmony_ci			goto interrupt_urb;
171962306a36Sopenharmony_ci		}
172062306a36Sopenharmony_ci		num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE);
172162306a36Sopenharmony_ci		if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
172262306a36Sopenharmony_ci			num_sgs = 0;
172362306a36Sopenharmony_ci		if (ep->streams)
172462306a36Sopenharmony_ci			stream_id = uurb->stream_id;
172562306a36Sopenharmony_ci		break;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	case USBDEVFS_URB_TYPE_INTERRUPT:
172862306a36Sopenharmony_ci		if (!usb_endpoint_xfer_int(&ep->desc))
172962306a36Sopenharmony_ci			return -EINVAL;
173062306a36Sopenharmony_ci interrupt_urb:
173162306a36Sopenharmony_ci		if (!is_in)
173262306a36Sopenharmony_ci			allow_zero = true;
173362306a36Sopenharmony_ci		else
173462306a36Sopenharmony_ci			allow_short = true;
173562306a36Sopenharmony_ci		break;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	case USBDEVFS_URB_TYPE_ISO:
173862306a36Sopenharmony_ci		/* arbitrary limit */
173962306a36Sopenharmony_ci		if (uurb->number_of_packets < 1 ||
174062306a36Sopenharmony_ci		    uurb->number_of_packets > 128)
174162306a36Sopenharmony_ci			return -EINVAL;
174262306a36Sopenharmony_ci		if (!usb_endpoint_xfer_isoc(&ep->desc))
174362306a36Sopenharmony_ci			return -EINVAL;
174462306a36Sopenharmony_ci		number_of_packets = uurb->number_of_packets;
174562306a36Sopenharmony_ci		isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
174662306a36Sopenharmony_ci				   number_of_packets;
174762306a36Sopenharmony_ci		isopkt = memdup_user(iso_frame_desc, isofrmlen);
174862306a36Sopenharmony_ci		if (IS_ERR(isopkt)) {
174962306a36Sopenharmony_ci			ret = PTR_ERR(isopkt);
175062306a36Sopenharmony_ci			isopkt = NULL;
175162306a36Sopenharmony_ci			goto error;
175262306a36Sopenharmony_ci		}
175362306a36Sopenharmony_ci		for (totlen = u = 0; u < number_of_packets; u++) {
175462306a36Sopenharmony_ci			/*
175562306a36Sopenharmony_ci			 * arbitrary limit need for USB 3.1 Gen2
175662306a36Sopenharmony_ci			 * sizemax: 96 DPs at SSP, 96 * 1024 = 98304
175762306a36Sopenharmony_ci			 */
175862306a36Sopenharmony_ci			if (isopkt[u].length > 98304) {
175962306a36Sopenharmony_ci				ret = -EINVAL;
176062306a36Sopenharmony_ci				goto error;
176162306a36Sopenharmony_ci			}
176262306a36Sopenharmony_ci			totlen += isopkt[u].length;
176362306a36Sopenharmony_ci		}
176462306a36Sopenharmony_ci		u *= sizeof(struct usb_iso_packet_descriptor);
176562306a36Sopenharmony_ci		uurb->buffer_length = totlen;
176662306a36Sopenharmony_ci		break;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	default:
176962306a36Sopenharmony_ci		return -EINVAL;
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	if (uurb->buffer_length > 0 &&
177362306a36Sopenharmony_ci			!access_ok(uurb->buffer, uurb->buffer_length)) {
177462306a36Sopenharmony_ci		ret = -EFAULT;
177562306a36Sopenharmony_ci		goto error;
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci	as = alloc_async(number_of_packets);
177862306a36Sopenharmony_ci	if (!as) {
177962306a36Sopenharmony_ci		ret = -ENOMEM;
178062306a36Sopenharmony_ci		goto error;
178162306a36Sopenharmony_ci	}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	as->usbm = find_memory_area(ps, uurb);
178462306a36Sopenharmony_ci	if (IS_ERR(as->usbm)) {
178562306a36Sopenharmony_ci		ret = PTR_ERR(as->usbm);
178662306a36Sopenharmony_ci		as->usbm = NULL;
178762306a36Sopenharmony_ci		goto error;
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	/* do not use SG buffers when memory mapped segments
179162306a36Sopenharmony_ci	 * are in use
179262306a36Sopenharmony_ci	 */
179362306a36Sopenharmony_ci	if (as->usbm)
179462306a36Sopenharmony_ci		num_sgs = 0;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	u += sizeof(struct async) + sizeof(struct urb) +
179762306a36Sopenharmony_ci	     (as->usbm ? 0 : uurb->buffer_length) +
179862306a36Sopenharmony_ci	     num_sgs * sizeof(struct scatterlist);
179962306a36Sopenharmony_ci	ret = usbfs_increase_memory_usage(u);
180062306a36Sopenharmony_ci	if (ret)
180162306a36Sopenharmony_ci		goto error;
180262306a36Sopenharmony_ci	as->mem_usage = u;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (num_sgs) {
180562306a36Sopenharmony_ci		as->urb->sg = kmalloc_array(num_sgs,
180662306a36Sopenharmony_ci					    sizeof(struct scatterlist),
180762306a36Sopenharmony_ci					    GFP_KERNEL | __GFP_NOWARN);
180862306a36Sopenharmony_ci		if (!as->urb->sg) {
180962306a36Sopenharmony_ci			ret = -ENOMEM;
181062306a36Sopenharmony_ci			goto error;
181162306a36Sopenharmony_ci		}
181262306a36Sopenharmony_ci		as->urb->num_sgs = num_sgs;
181362306a36Sopenharmony_ci		sg_init_table(as->urb->sg, as->urb->num_sgs);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci		totlen = uurb->buffer_length;
181662306a36Sopenharmony_ci		for (i = 0; i < as->urb->num_sgs; i++) {
181762306a36Sopenharmony_ci			u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen;
181862306a36Sopenharmony_ci			buf = kmalloc(u, GFP_KERNEL);
181962306a36Sopenharmony_ci			if (!buf) {
182062306a36Sopenharmony_ci				ret = -ENOMEM;
182162306a36Sopenharmony_ci				goto error;
182262306a36Sopenharmony_ci			}
182362306a36Sopenharmony_ci			sg_set_buf(&as->urb->sg[i], buf, u);
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci			if (!is_in) {
182662306a36Sopenharmony_ci				if (copy_from_user(buf, uurb->buffer, u)) {
182762306a36Sopenharmony_ci					ret = -EFAULT;
182862306a36Sopenharmony_ci					goto error;
182962306a36Sopenharmony_ci				}
183062306a36Sopenharmony_ci				uurb->buffer += u;
183162306a36Sopenharmony_ci			}
183262306a36Sopenharmony_ci			totlen -= u;
183362306a36Sopenharmony_ci		}
183462306a36Sopenharmony_ci	} else if (uurb->buffer_length > 0) {
183562306a36Sopenharmony_ci		if (as->usbm) {
183662306a36Sopenharmony_ci			unsigned long uurb_start = (unsigned long)uurb->buffer;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci			as->urb->transfer_buffer = as->usbm->mem +
183962306a36Sopenharmony_ci					(uurb_start - as->usbm->vm_start);
184062306a36Sopenharmony_ci		} else {
184162306a36Sopenharmony_ci			as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
184262306a36Sopenharmony_ci					GFP_KERNEL | __GFP_NOWARN);
184362306a36Sopenharmony_ci			if (!as->urb->transfer_buffer) {
184462306a36Sopenharmony_ci				ret = -ENOMEM;
184562306a36Sopenharmony_ci				goto error;
184662306a36Sopenharmony_ci			}
184762306a36Sopenharmony_ci			if (!is_in) {
184862306a36Sopenharmony_ci				if (copy_from_user(as->urb->transfer_buffer,
184962306a36Sopenharmony_ci						   uurb->buffer,
185062306a36Sopenharmony_ci						   uurb->buffer_length)) {
185162306a36Sopenharmony_ci					ret = -EFAULT;
185262306a36Sopenharmony_ci					goto error;
185362306a36Sopenharmony_ci				}
185462306a36Sopenharmony_ci			} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
185562306a36Sopenharmony_ci				/*
185662306a36Sopenharmony_ci				 * Isochronous input data may end up being
185762306a36Sopenharmony_ci				 * discontiguous if some of the packets are
185862306a36Sopenharmony_ci				 * short. Clear the buffer so that the gaps
185962306a36Sopenharmony_ci				 * don't leak kernel data to userspace.
186062306a36Sopenharmony_ci				 */
186162306a36Sopenharmony_ci				memset(as->urb->transfer_buffer, 0,
186262306a36Sopenharmony_ci						uurb->buffer_length);
186362306a36Sopenharmony_ci			}
186462306a36Sopenharmony_ci		}
186562306a36Sopenharmony_ci	}
186662306a36Sopenharmony_ci	as->urb->dev = ps->dev;
186762306a36Sopenharmony_ci	as->urb->pipe = (uurb->type << 30) |
186862306a36Sopenharmony_ci			__create_pipe(ps->dev, uurb->endpoint & 0xf) |
186962306a36Sopenharmony_ci			(uurb->endpoint & USB_DIR_IN);
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	/* This tedious sequence is necessary because the URB_* flags
187262306a36Sopenharmony_ci	 * are internal to the kernel and subject to change, whereas
187362306a36Sopenharmony_ci	 * the USBDEVFS_URB_* flags are a user API and must not be changed.
187462306a36Sopenharmony_ci	 */
187562306a36Sopenharmony_ci	u = (is_in ? URB_DIR_IN : URB_DIR_OUT);
187662306a36Sopenharmony_ci	if (uurb->flags & USBDEVFS_URB_ISO_ASAP)
187762306a36Sopenharmony_ci		u |= URB_ISO_ASAP;
187862306a36Sopenharmony_ci	if (allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
187962306a36Sopenharmony_ci		u |= URB_SHORT_NOT_OK;
188062306a36Sopenharmony_ci	if (allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET)
188162306a36Sopenharmony_ci		u |= URB_ZERO_PACKET;
188262306a36Sopenharmony_ci	if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT)
188362306a36Sopenharmony_ci		u |= URB_NO_INTERRUPT;
188462306a36Sopenharmony_ci	as->urb->transfer_flags = u;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	if (!allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
188762306a36Sopenharmony_ci		dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_SHORT_NOT_OK.\n");
188862306a36Sopenharmony_ci	if (!allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET)
188962306a36Sopenharmony_ci		dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_ZERO_PACKET.\n");
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	as->urb->transfer_buffer_length = uurb->buffer_length;
189262306a36Sopenharmony_ci	as->urb->setup_packet = (unsigned char *)dr;
189362306a36Sopenharmony_ci	dr = NULL;
189462306a36Sopenharmony_ci	as->urb->start_frame = uurb->start_frame;
189562306a36Sopenharmony_ci	as->urb->number_of_packets = number_of_packets;
189662306a36Sopenharmony_ci	as->urb->stream_id = stream_id;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	if (ep->desc.bInterval) {
189962306a36Sopenharmony_ci		if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
190062306a36Sopenharmony_ci				ps->dev->speed == USB_SPEED_HIGH ||
190162306a36Sopenharmony_ci				ps->dev->speed >= USB_SPEED_SUPER)
190262306a36Sopenharmony_ci			as->urb->interval = 1 <<
190362306a36Sopenharmony_ci					min(15, ep->desc.bInterval - 1);
190462306a36Sopenharmony_ci		else
190562306a36Sopenharmony_ci			as->urb->interval = ep->desc.bInterval;
190662306a36Sopenharmony_ci	}
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	as->urb->context = as;
190962306a36Sopenharmony_ci	as->urb->complete = async_completed;
191062306a36Sopenharmony_ci	for (totlen = u = 0; u < number_of_packets; u++) {
191162306a36Sopenharmony_ci		as->urb->iso_frame_desc[u].offset = totlen;
191262306a36Sopenharmony_ci		as->urb->iso_frame_desc[u].length = isopkt[u].length;
191362306a36Sopenharmony_ci		totlen += isopkt[u].length;
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci	kfree(isopkt);
191662306a36Sopenharmony_ci	isopkt = NULL;
191762306a36Sopenharmony_ci	as->ps = ps;
191862306a36Sopenharmony_ci	as->userurb = arg;
191962306a36Sopenharmony_ci	as->userurb_sigval = userurb_sigval;
192062306a36Sopenharmony_ci	if (as->usbm) {
192162306a36Sopenharmony_ci		unsigned long uurb_start = (unsigned long)uurb->buffer;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci		as->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
192462306a36Sopenharmony_ci		as->urb->transfer_dma = as->usbm->dma_handle +
192562306a36Sopenharmony_ci				(uurb_start - as->usbm->vm_start);
192662306a36Sopenharmony_ci	} else if (is_in && uurb->buffer_length > 0)
192762306a36Sopenharmony_ci		as->userbuffer = uurb->buffer;
192862306a36Sopenharmony_ci	as->signr = uurb->signr;
192962306a36Sopenharmony_ci	as->ifnum = ifnum;
193062306a36Sopenharmony_ci	as->pid = get_pid(task_pid(current));
193162306a36Sopenharmony_ci	as->cred = get_current_cred();
193262306a36Sopenharmony_ci	snoop_urb(ps->dev, as->userurb, as->urb->pipe,
193362306a36Sopenharmony_ci			as->urb->transfer_buffer_length, 0, SUBMIT,
193462306a36Sopenharmony_ci			NULL, 0);
193562306a36Sopenharmony_ci	if (!is_in)
193662306a36Sopenharmony_ci		snoop_urb_data(as->urb, as->urb->transfer_buffer_length);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	async_newpending(as);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	if (usb_endpoint_xfer_bulk(&ep->desc)) {
194162306a36Sopenharmony_ci		spin_lock_irq(&ps->lock);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci		/* Not exactly the endpoint address; the direction bit is
194462306a36Sopenharmony_ci		 * shifted to the 0x10 position so that the value will be
194562306a36Sopenharmony_ci		 * between 0 and 31.
194662306a36Sopenharmony_ci		 */
194762306a36Sopenharmony_ci		as->bulk_addr = usb_endpoint_num(&ep->desc) |
194862306a36Sopenharmony_ci			((ep->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
194962306a36Sopenharmony_ci				>> 3);
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci		/* If this bulk URB is the start of a new transfer, re-enable
195262306a36Sopenharmony_ci		 * the endpoint.  Otherwise mark it as a continuation URB.
195362306a36Sopenharmony_ci		 */
195462306a36Sopenharmony_ci		if (uurb->flags & USBDEVFS_URB_BULK_CONTINUATION)
195562306a36Sopenharmony_ci			as->bulk_status = AS_CONTINUATION;
195662306a36Sopenharmony_ci		else
195762306a36Sopenharmony_ci			ps->disabled_bulk_eps &= ~(1 << as->bulk_addr);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci		/* Don't accept continuation URBs if the endpoint is
196062306a36Sopenharmony_ci		 * disabled because of an earlier error.
196162306a36Sopenharmony_ci		 */
196262306a36Sopenharmony_ci		if (ps->disabled_bulk_eps & (1 << as->bulk_addr))
196362306a36Sopenharmony_ci			ret = -EREMOTEIO;
196462306a36Sopenharmony_ci		else
196562306a36Sopenharmony_ci			ret = usb_submit_urb(as->urb, GFP_ATOMIC);
196662306a36Sopenharmony_ci		spin_unlock_irq(&ps->lock);
196762306a36Sopenharmony_ci	} else {
196862306a36Sopenharmony_ci		ret = usb_submit_urb(as->urb, GFP_KERNEL);
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	if (ret) {
197262306a36Sopenharmony_ci		dev_printk(KERN_DEBUG, &ps->dev->dev,
197362306a36Sopenharmony_ci			   "usbfs: usb_submit_urb returned %d\n", ret);
197462306a36Sopenharmony_ci		snoop_urb(ps->dev, as->userurb, as->urb->pipe,
197562306a36Sopenharmony_ci				0, ret, COMPLETE, NULL, 0);
197662306a36Sopenharmony_ci		async_removepending(as);
197762306a36Sopenharmony_ci		goto error;
197862306a36Sopenharmony_ci	}
197962306a36Sopenharmony_ci	return 0;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci error:
198262306a36Sopenharmony_ci	kfree(isopkt);
198362306a36Sopenharmony_ci	kfree(dr);
198462306a36Sopenharmony_ci	if (as)
198562306a36Sopenharmony_ci		free_async(as);
198662306a36Sopenharmony_ci	return ret;
198762306a36Sopenharmony_ci}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_cistatic int proc_submiturb(struct usb_dev_state *ps, void __user *arg)
199062306a36Sopenharmony_ci{
199162306a36Sopenharmony_ci	struct usbdevfs_urb uurb;
199262306a36Sopenharmony_ci	sigval_t userurb_sigval;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	if (copy_from_user(&uurb, arg, sizeof(uurb)))
199562306a36Sopenharmony_ci		return -EFAULT;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	memset(&userurb_sigval, 0, sizeof(userurb_sigval));
199862306a36Sopenharmony_ci	userurb_sigval.sival_ptr = arg;
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	return proc_do_submiturb(ps, &uurb,
200162306a36Sopenharmony_ci			(((struct usbdevfs_urb __user *)arg)->iso_frame_desc),
200262306a36Sopenharmony_ci			arg, userurb_sigval);
200362306a36Sopenharmony_ci}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cistatic int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg)
200662306a36Sopenharmony_ci{
200762306a36Sopenharmony_ci	struct urb *urb;
200862306a36Sopenharmony_ci	struct async *as;
200962306a36Sopenharmony_ci	unsigned long flags;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	spin_lock_irqsave(&ps->lock, flags);
201262306a36Sopenharmony_ci	as = async_getpending(ps, arg);
201362306a36Sopenharmony_ci	if (!as) {
201462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ps->lock, flags);
201562306a36Sopenharmony_ci		return -EINVAL;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	urb = as->urb;
201962306a36Sopenharmony_ci	usb_get_urb(urb);
202062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps->lock, flags);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	usb_kill_urb(urb);
202362306a36Sopenharmony_ci	usb_put_urb(urb);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	return 0;
202662306a36Sopenharmony_ci}
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_cistatic void compute_isochronous_actual_length(struct urb *urb)
202962306a36Sopenharmony_ci{
203062306a36Sopenharmony_ci	unsigned int i;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	if (urb->number_of_packets > 0) {
203362306a36Sopenharmony_ci		urb->actual_length = 0;
203462306a36Sopenharmony_ci		for (i = 0; i < urb->number_of_packets; i++)
203562306a36Sopenharmony_ci			urb->actual_length +=
203662306a36Sopenharmony_ci					urb->iso_frame_desc[i].actual_length;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci}
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_cistatic int processcompl(struct async *as, void __user * __user *arg)
204162306a36Sopenharmony_ci{
204262306a36Sopenharmony_ci	struct urb *urb = as->urb;
204362306a36Sopenharmony_ci	struct usbdevfs_urb __user *userurb = as->userurb;
204462306a36Sopenharmony_ci	void __user *addr = as->userurb;
204562306a36Sopenharmony_ci	unsigned int i;
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	compute_isochronous_actual_length(urb);
204862306a36Sopenharmony_ci	if (as->userbuffer && urb->actual_length) {
204962306a36Sopenharmony_ci		if (copy_urb_data_to_user(as->userbuffer, urb))
205062306a36Sopenharmony_ci			goto err_out;
205162306a36Sopenharmony_ci	}
205262306a36Sopenharmony_ci	if (put_user(as->status, &userurb->status))
205362306a36Sopenharmony_ci		goto err_out;
205462306a36Sopenharmony_ci	if (put_user(urb->actual_length, &userurb->actual_length))
205562306a36Sopenharmony_ci		goto err_out;
205662306a36Sopenharmony_ci	if (put_user(urb->error_count, &userurb->error_count))
205762306a36Sopenharmony_ci		goto err_out;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
206062306a36Sopenharmony_ci		for (i = 0; i < urb->number_of_packets; i++) {
206162306a36Sopenharmony_ci			if (put_user(urb->iso_frame_desc[i].actual_length,
206262306a36Sopenharmony_ci				     &userurb->iso_frame_desc[i].actual_length))
206362306a36Sopenharmony_ci				goto err_out;
206462306a36Sopenharmony_ci			if (put_user(urb->iso_frame_desc[i].status,
206562306a36Sopenharmony_ci				     &userurb->iso_frame_desc[i].status))
206662306a36Sopenharmony_ci				goto err_out;
206762306a36Sopenharmony_ci		}
206862306a36Sopenharmony_ci	}
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	if (put_user(addr, (void __user * __user *)arg))
207162306a36Sopenharmony_ci		return -EFAULT;
207262306a36Sopenharmony_ci	return 0;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_cierr_out:
207562306a36Sopenharmony_ci	return -EFAULT;
207662306a36Sopenharmony_ci}
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_cistatic struct async *reap_as(struct usb_dev_state *ps)
207962306a36Sopenharmony_ci{
208062306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
208162306a36Sopenharmony_ci	struct async *as = NULL;
208262306a36Sopenharmony_ci	struct usb_device *dev = ps->dev;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	add_wait_queue(&ps->wait, &wait);
208562306a36Sopenharmony_ci	for (;;) {
208662306a36Sopenharmony_ci		__set_current_state(TASK_INTERRUPTIBLE);
208762306a36Sopenharmony_ci		as = async_getcompleted(ps);
208862306a36Sopenharmony_ci		if (as || !connected(ps))
208962306a36Sopenharmony_ci			break;
209062306a36Sopenharmony_ci		if (signal_pending(current))
209162306a36Sopenharmony_ci			break;
209262306a36Sopenharmony_ci		usb_unlock_device(dev);
209362306a36Sopenharmony_ci		schedule();
209462306a36Sopenharmony_ci		usb_lock_device(dev);
209562306a36Sopenharmony_ci	}
209662306a36Sopenharmony_ci	remove_wait_queue(&ps->wait, &wait);
209762306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
209862306a36Sopenharmony_ci	return as;
209962306a36Sopenharmony_ci}
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_cistatic int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
210262306a36Sopenharmony_ci{
210362306a36Sopenharmony_ci	struct async *as = reap_as(ps);
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	if (as) {
210662306a36Sopenharmony_ci		int retval;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci		snoop(&ps->dev->dev, "reap %px\n", as->userurb);
210962306a36Sopenharmony_ci		retval = processcompl(as, (void __user * __user *)arg);
211062306a36Sopenharmony_ci		free_async(as);
211162306a36Sopenharmony_ci		return retval;
211262306a36Sopenharmony_ci	}
211362306a36Sopenharmony_ci	if (signal_pending(current))
211462306a36Sopenharmony_ci		return -EINTR;
211562306a36Sopenharmony_ci	return -ENODEV;
211662306a36Sopenharmony_ci}
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_cistatic int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
211962306a36Sopenharmony_ci{
212062306a36Sopenharmony_ci	int retval;
212162306a36Sopenharmony_ci	struct async *as;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	as = async_getcompleted(ps);
212462306a36Sopenharmony_ci	if (as) {
212562306a36Sopenharmony_ci		snoop(&ps->dev->dev, "reap %px\n", as->userurb);
212662306a36Sopenharmony_ci		retval = processcompl(as, (void __user * __user *)arg);
212762306a36Sopenharmony_ci		free_async(as);
212862306a36Sopenharmony_ci	} else {
212962306a36Sopenharmony_ci		retval = (connected(ps) ? -EAGAIN : -ENODEV);
213062306a36Sopenharmony_ci	}
213162306a36Sopenharmony_ci	return retval;
213262306a36Sopenharmony_ci}
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
213562306a36Sopenharmony_cistatic int proc_control_compat(struct usb_dev_state *ps,
213662306a36Sopenharmony_ci				struct usbdevfs_ctrltransfer32 __user *p32)
213762306a36Sopenharmony_ci{
213862306a36Sopenharmony_ci	struct usbdevfs_ctrltransfer ctrl;
213962306a36Sopenharmony_ci	u32 udata;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	if (copy_from_user(&ctrl, p32, sizeof(*p32) - sizeof(compat_caddr_t)) ||
214262306a36Sopenharmony_ci	    get_user(udata, &p32->data))
214362306a36Sopenharmony_ci		return -EFAULT;
214462306a36Sopenharmony_ci	ctrl.data = compat_ptr(udata);
214562306a36Sopenharmony_ci	return do_proc_control(ps, &ctrl);
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int proc_bulk_compat(struct usb_dev_state *ps,
214962306a36Sopenharmony_ci			struct usbdevfs_bulktransfer32 __user *p32)
215062306a36Sopenharmony_ci{
215162306a36Sopenharmony_ci	struct usbdevfs_bulktransfer bulk;
215262306a36Sopenharmony_ci	compat_caddr_t addr;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	if (get_user(bulk.ep, &p32->ep) ||
215562306a36Sopenharmony_ci	    get_user(bulk.len, &p32->len) ||
215662306a36Sopenharmony_ci	    get_user(bulk.timeout, &p32->timeout) ||
215762306a36Sopenharmony_ci	    get_user(addr, &p32->data))
215862306a36Sopenharmony_ci		return -EFAULT;
215962306a36Sopenharmony_ci	bulk.data = compat_ptr(addr);
216062306a36Sopenharmony_ci	return do_proc_bulk(ps, &bulk);
216162306a36Sopenharmony_ci}
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_cistatic int proc_disconnectsignal_compat(struct usb_dev_state *ps, void __user *arg)
216462306a36Sopenharmony_ci{
216562306a36Sopenharmony_ci	struct usbdevfs_disconnectsignal32 ds;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	if (copy_from_user(&ds, arg, sizeof(ds)))
216862306a36Sopenharmony_ci		return -EFAULT;
216962306a36Sopenharmony_ci	ps->discsignr = ds.signr;
217062306a36Sopenharmony_ci	ps->disccontext.sival_int = ds.context;
217162306a36Sopenharmony_ci	return 0;
217262306a36Sopenharmony_ci}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_cistatic int get_urb32(struct usbdevfs_urb *kurb,
217562306a36Sopenharmony_ci		     struct usbdevfs_urb32 __user *uurb)
217662306a36Sopenharmony_ci{
217762306a36Sopenharmony_ci	struct usbdevfs_urb32 urb32;
217862306a36Sopenharmony_ci	if (copy_from_user(&urb32, uurb, sizeof(*uurb)))
217962306a36Sopenharmony_ci		return -EFAULT;
218062306a36Sopenharmony_ci	kurb->type = urb32.type;
218162306a36Sopenharmony_ci	kurb->endpoint = urb32.endpoint;
218262306a36Sopenharmony_ci	kurb->status = urb32.status;
218362306a36Sopenharmony_ci	kurb->flags = urb32.flags;
218462306a36Sopenharmony_ci	kurb->buffer = compat_ptr(urb32.buffer);
218562306a36Sopenharmony_ci	kurb->buffer_length = urb32.buffer_length;
218662306a36Sopenharmony_ci	kurb->actual_length = urb32.actual_length;
218762306a36Sopenharmony_ci	kurb->start_frame = urb32.start_frame;
218862306a36Sopenharmony_ci	kurb->number_of_packets = urb32.number_of_packets;
218962306a36Sopenharmony_ci	kurb->error_count = urb32.error_count;
219062306a36Sopenharmony_ci	kurb->signr = urb32.signr;
219162306a36Sopenharmony_ci	kurb->usercontext = compat_ptr(urb32.usercontext);
219262306a36Sopenharmony_ci	return 0;
219362306a36Sopenharmony_ci}
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_cistatic int proc_submiturb_compat(struct usb_dev_state *ps, void __user *arg)
219662306a36Sopenharmony_ci{
219762306a36Sopenharmony_ci	struct usbdevfs_urb uurb;
219862306a36Sopenharmony_ci	sigval_t userurb_sigval;
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	if (get_urb32(&uurb, (struct usbdevfs_urb32 __user *)arg))
220162306a36Sopenharmony_ci		return -EFAULT;
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	memset(&userurb_sigval, 0, sizeof(userurb_sigval));
220462306a36Sopenharmony_ci	userurb_sigval.sival_int = ptr_to_compat(arg);
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	return proc_do_submiturb(ps, &uurb,
220762306a36Sopenharmony_ci			((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc,
220862306a36Sopenharmony_ci			arg, userurb_sigval);
220962306a36Sopenharmony_ci}
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_cistatic int processcompl_compat(struct async *as, void __user * __user *arg)
221262306a36Sopenharmony_ci{
221362306a36Sopenharmony_ci	struct urb *urb = as->urb;
221462306a36Sopenharmony_ci	struct usbdevfs_urb32 __user *userurb = as->userurb;
221562306a36Sopenharmony_ci	void __user *addr = as->userurb;
221662306a36Sopenharmony_ci	unsigned int i;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	compute_isochronous_actual_length(urb);
221962306a36Sopenharmony_ci	if (as->userbuffer && urb->actual_length) {
222062306a36Sopenharmony_ci		if (copy_urb_data_to_user(as->userbuffer, urb))
222162306a36Sopenharmony_ci			return -EFAULT;
222262306a36Sopenharmony_ci	}
222362306a36Sopenharmony_ci	if (put_user(as->status, &userurb->status))
222462306a36Sopenharmony_ci		return -EFAULT;
222562306a36Sopenharmony_ci	if (put_user(urb->actual_length, &userurb->actual_length))
222662306a36Sopenharmony_ci		return -EFAULT;
222762306a36Sopenharmony_ci	if (put_user(urb->error_count, &userurb->error_count))
222862306a36Sopenharmony_ci		return -EFAULT;
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
223162306a36Sopenharmony_ci		for (i = 0; i < urb->number_of_packets; i++) {
223262306a36Sopenharmony_ci			if (put_user(urb->iso_frame_desc[i].actual_length,
223362306a36Sopenharmony_ci				     &userurb->iso_frame_desc[i].actual_length))
223462306a36Sopenharmony_ci				return -EFAULT;
223562306a36Sopenharmony_ci			if (put_user(urb->iso_frame_desc[i].status,
223662306a36Sopenharmony_ci				     &userurb->iso_frame_desc[i].status))
223762306a36Sopenharmony_ci				return -EFAULT;
223862306a36Sopenharmony_ci		}
223962306a36Sopenharmony_ci	}
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	if (put_user(ptr_to_compat(addr), (u32 __user *)arg))
224262306a36Sopenharmony_ci		return -EFAULT;
224362306a36Sopenharmony_ci	return 0;
224462306a36Sopenharmony_ci}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_cistatic int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
224762306a36Sopenharmony_ci{
224862306a36Sopenharmony_ci	struct async *as = reap_as(ps);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	if (as) {
225162306a36Sopenharmony_ci		int retval;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci		snoop(&ps->dev->dev, "reap %px\n", as->userurb);
225462306a36Sopenharmony_ci		retval = processcompl_compat(as, (void __user * __user *)arg);
225562306a36Sopenharmony_ci		free_async(as);
225662306a36Sopenharmony_ci		return retval;
225762306a36Sopenharmony_ci	}
225862306a36Sopenharmony_ci	if (signal_pending(current))
225962306a36Sopenharmony_ci		return -EINTR;
226062306a36Sopenharmony_ci	return -ENODEV;
226162306a36Sopenharmony_ci}
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_cistatic int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	int retval;
226662306a36Sopenharmony_ci	struct async *as;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	as = async_getcompleted(ps);
226962306a36Sopenharmony_ci	if (as) {
227062306a36Sopenharmony_ci		snoop(&ps->dev->dev, "reap %px\n", as->userurb);
227162306a36Sopenharmony_ci		retval = processcompl_compat(as, (void __user * __user *)arg);
227262306a36Sopenharmony_ci		free_async(as);
227362306a36Sopenharmony_ci	} else {
227462306a36Sopenharmony_ci		retval = (connected(ps) ? -EAGAIN : -ENODEV);
227562306a36Sopenharmony_ci	}
227662306a36Sopenharmony_ci	return retval;
227762306a36Sopenharmony_ci}
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci#endif
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_cistatic int proc_disconnectsignal(struct usb_dev_state *ps, void __user *arg)
228362306a36Sopenharmony_ci{
228462306a36Sopenharmony_ci	struct usbdevfs_disconnectsignal ds;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	if (copy_from_user(&ds, arg, sizeof(ds)))
228762306a36Sopenharmony_ci		return -EFAULT;
228862306a36Sopenharmony_ci	ps->discsignr = ds.signr;
228962306a36Sopenharmony_ci	ps->disccontext.sival_ptr = ds.context;
229062306a36Sopenharmony_ci	return 0;
229162306a36Sopenharmony_ci}
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_cistatic int proc_claiminterface(struct usb_dev_state *ps, void __user *arg)
229462306a36Sopenharmony_ci{
229562306a36Sopenharmony_ci	unsigned int ifnum;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	if (get_user(ifnum, (unsigned int __user *)arg))
229862306a36Sopenharmony_ci		return -EFAULT;
229962306a36Sopenharmony_ci	return claimintf(ps, ifnum);
230062306a36Sopenharmony_ci}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_cistatic int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg)
230362306a36Sopenharmony_ci{
230462306a36Sopenharmony_ci	unsigned int ifnum;
230562306a36Sopenharmony_ci	int ret;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	if (get_user(ifnum, (unsigned int __user *)arg))
230862306a36Sopenharmony_ci		return -EFAULT;
230962306a36Sopenharmony_ci	ret = releaseintf(ps, ifnum);
231062306a36Sopenharmony_ci	if (ret < 0)
231162306a36Sopenharmony_ci		return ret;
231262306a36Sopenharmony_ci	destroy_async_on_interface(ps, ifnum);
231362306a36Sopenharmony_ci	return 0;
231462306a36Sopenharmony_ci}
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_cistatic int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
231762306a36Sopenharmony_ci{
231862306a36Sopenharmony_ci	int			size;
231962306a36Sopenharmony_ci	void			*buf = NULL;
232062306a36Sopenharmony_ci	int			retval = 0;
232162306a36Sopenharmony_ci	struct usb_interface    *intf = NULL;
232262306a36Sopenharmony_ci	struct usb_driver       *driver = NULL;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	if (ps->privileges_dropped)
232562306a36Sopenharmony_ci		return -EACCES;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	if (!connected(ps))
232862306a36Sopenharmony_ci		return -ENODEV;
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	/* alloc buffer */
233162306a36Sopenharmony_ci	size = _IOC_SIZE(ctl->ioctl_code);
233262306a36Sopenharmony_ci	if (size > 0) {
233362306a36Sopenharmony_ci		buf = kmalloc(size, GFP_KERNEL);
233462306a36Sopenharmony_ci		if (buf == NULL)
233562306a36Sopenharmony_ci			return -ENOMEM;
233662306a36Sopenharmony_ci		if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) {
233762306a36Sopenharmony_ci			if (copy_from_user(buf, ctl->data, size)) {
233862306a36Sopenharmony_ci				kfree(buf);
233962306a36Sopenharmony_ci				return -EFAULT;
234062306a36Sopenharmony_ci			}
234162306a36Sopenharmony_ci		} else {
234262306a36Sopenharmony_ci			memset(buf, 0, size);
234362306a36Sopenharmony_ci		}
234462306a36Sopenharmony_ci	}
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	if (ps->dev->state != USB_STATE_CONFIGURED)
234762306a36Sopenharmony_ci		retval = -EHOSTUNREACH;
234862306a36Sopenharmony_ci	else if (!(intf = usb_ifnum_to_if(ps->dev, ctl->ifno)))
234962306a36Sopenharmony_ci		retval = -EINVAL;
235062306a36Sopenharmony_ci	else switch (ctl->ioctl_code) {
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	/* disconnect kernel driver from interface */
235362306a36Sopenharmony_ci	case USBDEVFS_DISCONNECT:
235462306a36Sopenharmony_ci		if (intf->dev.driver) {
235562306a36Sopenharmony_ci			driver = to_usb_driver(intf->dev.driver);
235662306a36Sopenharmony_ci			dev_dbg(&intf->dev, "disconnect by usbfs\n");
235762306a36Sopenharmony_ci			usb_driver_release_interface(driver, intf);
235862306a36Sopenharmony_ci		} else
235962306a36Sopenharmony_ci			retval = -ENODATA;
236062306a36Sopenharmony_ci		break;
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	/* let kernel drivers try to (re)bind to the interface */
236362306a36Sopenharmony_ci	case USBDEVFS_CONNECT:
236462306a36Sopenharmony_ci		if (!intf->dev.driver)
236562306a36Sopenharmony_ci			retval = device_attach(&intf->dev);
236662306a36Sopenharmony_ci		else
236762306a36Sopenharmony_ci			retval = -EBUSY;
236862306a36Sopenharmony_ci		break;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	/* talk directly to the interface's driver */
237162306a36Sopenharmony_ci	default:
237262306a36Sopenharmony_ci		if (intf->dev.driver)
237362306a36Sopenharmony_ci			driver = to_usb_driver(intf->dev.driver);
237462306a36Sopenharmony_ci		if (driver == NULL || driver->unlocked_ioctl == NULL) {
237562306a36Sopenharmony_ci			retval = -ENOTTY;
237662306a36Sopenharmony_ci		} else {
237762306a36Sopenharmony_ci			retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf);
237862306a36Sopenharmony_ci			if (retval == -ENOIOCTLCMD)
237962306a36Sopenharmony_ci				retval = -ENOTTY;
238062306a36Sopenharmony_ci		}
238162306a36Sopenharmony_ci	}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	/* cleanup and return */
238462306a36Sopenharmony_ci	if (retval >= 0
238562306a36Sopenharmony_ci			&& (_IOC_DIR(ctl->ioctl_code) & _IOC_READ) != 0
238662306a36Sopenharmony_ci			&& size > 0
238762306a36Sopenharmony_ci			&& copy_to_user(ctl->data, buf, size) != 0)
238862306a36Sopenharmony_ci		retval = -EFAULT;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	kfree(buf);
239162306a36Sopenharmony_ci	return retval;
239262306a36Sopenharmony_ci}
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_cistatic int proc_ioctl_default(struct usb_dev_state *ps, void __user *arg)
239562306a36Sopenharmony_ci{
239662306a36Sopenharmony_ci	struct usbdevfs_ioctl	ctrl;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
239962306a36Sopenharmony_ci		return -EFAULT;
240062306a36Sopenharmony_ci	return proc_ioctl(ps, &ctrl);
240162306a36Sopenharmony_ci}
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
240462306a36Sopenharmony_cistatic int proc_ioctl_compat(struct usb_dev_state *ps, compat_uptr_t arg)
240562306a36Sopenharmony_ci{
240662306a36Sopenharmony_ci	struct usbdevfs_ioctl32 ioc32;
240762306a36Sopenharmony_ci	struct usbdevfs_ioctl ctrl;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	if (copy_from_user(&ioc32, compat_ptr(arg), sizeof(ioc32)))
241062306a36Sopenharmony_ci		return -EFAULT;
241162306a36Sopenharmony_ci	ctrl.ifno = ioc32.ifno;
241262306a36Sopenharmony_ci	ctrl.ioctl_code = ioc32.ioctl_code;
241362306a36Sopenharmony_ci	ctrl.data = compat_ptr(ioc32.data);
241462306a36Sopenharmony_ci	return proc_ioctl(ps, &ctrl);
241562306a36Sopenharmony_ci}
241662306a36Sopenharmony_ci#endif
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_cistatic int proc_claim_port(struct usb_dev_state *ps, void __user *arg)
241962306a36Sopenharmony_ci{
242062306a36Sopenharmony_ci	unsigned portnum;
242162306a36Sopenharmony_ci	int rc;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	if (get_user(portnum, (unsigned __user *) arg))
242462306a36Sopenharmony_ci		return -EFAULT;
242562306a36Sopenharmony_ci	rc = usb_hub_claim_port(ps->dev, portnum, ps);
242662306a36Sopenharmony_ci	if (rc == 0)
242762306a36Sopenharmony_ci		snoop(&ps->dev->dev, "port %d claimed by process %d: %s\n",
242862306a36Sopenharmony_ci			portnum, task_pid_nr(current), current->comm);
242962306a36Sopenharmony_ci	return rc;
243062306a36Sopenharmony_ci}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_cistatic int proc_release_port(struct usb_dev_state *ps, void __user *arg)
243362306a36Sopenharmony_ci{
243462306a36Sopenharmony_ci	unsigned portnum;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	if (get_user(portnum, (unsigned __user *) arg))
243762306a36Sopenharmony_ci		return -EFAULT;
243862306a36Sopenharmony_ci	return usb_hub_release_port(ps->dev, portnum, ps);
243962306a36Sopenharmony_ci}
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_cistatic int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
244262306a36Sopenharmony_ci{
244362306a36Sopenharmony_ci	__u32 caps;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
244662306a36Sopenharmony_ci			USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
244762306a36Sopenharmony_ci			USBDEVFS_CAP_DROP_PRIVILEGES |
244862306a36Sopenharmony_ci			USBDEVFS_CAP_CONNINFO_EX | MAYBE_CAP_SUSPEND;
244962306a36Sopenharmony_ci	if (!ps->dev->bus->no_stop_on_short)
245062306a36Sopenharmony_ci		caps |= USBDEVFS_CAP_BULK_CONTINUATION;
245162306a36Sopenharmony_ci	if (ps->dev->bus->sg_tablesize)
245262306a36Sopenharmony_ci		caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	if (put_user(caps, (__u32 __user *)arg))
245562306a36Sopenharmony_ci		return -EFAULT;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	return 0;
245862306a36Sopenharmony_ci}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_cistatic int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	struct usbdevfs_disconnect_claim dc;
246362306a36Sopenharmony_ci	struct usb_interface *intf;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	if (copy_from_user(&dc, arg, sizeof(dc)))
246662306a36Sopenharmony_ci		return -EFAULT;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	intf = usb_ifnum_to_if(ps->dev, dc.interface);
246962306a36Sopenharmony_ci	if (!intf)
247062306a36Sopenharmony_ci		return -EINVAL;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	if (intf->dev.driver) {
247362306a36Sopenharmony_ci		struct usb_driver *driver = to_usb_driver(intf->dev.driver);
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci		if (ps->privileges_dropped)
247662306a36Sopenharmony_ci			return -EACCES;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci		if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
247962306a36Sopenharmony_ci				strncmp(dc.driver, intf->dev.driver->name,
248062306a36Sopenharmony_ci					sizeof(dc.driver)) != 0)
248162306a36Sopenharmony_ci			return -EBUSY;
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci		if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) &&
248462306a36Sopenharmony_ci				strncmp(dc.driver, intf->dev.driver->name,
248562306a36Sopenharmony_ci					sizeof(dc.driver)) == 0)
248662306a36Sopenharmony_ci			return -EBUSY;
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci		dev_dbg(&intf->dev, "disconnect by usbfs\n");
248962306a36Sopenharmony_ci		usb_driver_release_interface(driver, intf);
249062306a36Sopenharmony_ci	}
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	return claimintf(ps, dc.interface);
249362306a36Sopenharmony_ci}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_cistatic int proc_alloc_streams(struct usb_dev_state *ps, void __user *arg)
249662306a36Sopenharmony_ci{
249762306a36Sopenharmony_ci	unsigned num_streams, num_eps;
249862306a36Sopenharmony_ci	struct usb_host_endpoint **eps;
249962306a36Sopenharmony_ci	struct usb_interface *intf;
250062306a36Sopenharmony_ci	int r;
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps,
250362306a36Sopenharmony_ci				   &eps, &intf);
250462306a36Sopenharmony_ci	if (r)
250562306a36Sopenharmony_ci		return r;
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	destroy_async_on_interface(ps,
250862306a36Sopenharmony_ci				   intf->altsetting[0].desc.bInterfaceNumber);
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL);
251162306a36Sopenharmony_ci	kfree(eps);
251262306a36Sopenharmony_ci	return r;
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_cistatic int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
251662306a36Sopenharmony_ci{
251762306a36Sopenharmony_ci	unsigned num_eps;
251862306a36Sopenharmony_ci	struct usb_host_endpoint **eps;
251962306a36Sopenharmony_ci	struct usb_interface *intf;
252062306a36Sopenharmony_ci	int r;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf);
252362306a36Sopenharmony_ci	if (r)
252462306a36Sopenharmony_ci		return r;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	destroy_async_on_interface(ps,
252762306a36Sopenharmony_ci				   intf->altsetting[0].desc.bInterfaceNumber);
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL);
253062306a36Sopenharmony_ci	kfree(eps);
253162306a36Sopenharmony_ci	return r;
253262306a36Sopenharmony_ci}
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_cistatic int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg)
253562306a36Sopenharmony_ci{
253662306a36Sopenharmony_ci	u32 data;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	if (copy_from_user(&data, arg, sizeof(data)))
253962306a36Sopenharmony_ci		return -EFAULT;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	/* This is a one way operation. Once privileges are
254262306a36Sopenharmony_ci	 * dropped, you cannot regain them. You may however reissue
254362306a36Sopenharmony_ci	 * this ioctl to shrink the allowed interfaces mask.
254462306a36Sopenharmony_ci	 */
254562306a36Sopenharmony_ci	ps->interface_allowed_mask &= data;
254662306a36Sopenharmony_ci	ps->privileges_dropped = true;
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	return 0;
254962306a36Sopenharmony_ci}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_cistatic int proc_forbid_suspend(struct usb_dev_state *ps)
255262306a36Sopenharmony_ci{
255362306a36Sopenharmony_ci	int ret = 0;
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	if (ps->suspend_allowed) {
255662306a36Sopenharmony_ci		ret = usb_autoresume_device(ps->dev);
255762306a36Sopenharmony_ci		if (ret == 0)
255862306a36Sopenharmony_ci			ps->suspend_allowed = false;
255962306a36Sopenharmony_ci		else if (ret != -ENODEV)
256062306a36Sopenharmony_ci			ret = -EIO;
256162306a36Sopenharmony_ci	}
256262306a36Sopenharmony_ci	return ret;
256362306a36Sopenharmony_ci}
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_cistatic int proc_allow_suspend(struct usb_dev_state *ps)
256662306a36Sopenharmony_ci{
256762306a36Sopenharmony_ci	if (!connected(ps))
256862306a36Sopenharmony_ci		return -ENODEV;
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	WRITE_ONCE(ps->not_yet_resumed, 1);
257162306a36Sopenharmony_ci	if (!ps->suspend_allowed) {
257262306a36Sopenharmony_ci		usb_autosuspend_device(ps->dev);
257362306a36Sopenharmony_ci		ps->suspend_allowed = true;
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci	return 0;
257662306a36Sopenharmony_ci}
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_cistatic int proc_wait_for_resume(struct usb_dev_state *ps)
257962306a36Sopenharmony_ci{
258062306a36Sopenharmony_ci	int ret;
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	usb_unlock_device(ps->dev);
258362306a36Sopenharmony_ci	ret = wait_event_interruptible(ps->wait_for_resume,
258462306a36Sopenharmony_ci			READ_ONCE(ps->not_yet_resumed) == 0);
258562306a36Sopenharmony_ci	usb_lock_device(ps->dev);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	if (ret != 0)
258862306a36Sopenharmony_ci		return -EINTR;
258962306a36Sopenharmony_ci	return proc_forbid_suspend(ps);
259062306a36Sopenharmony_ci}
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci/*
259362306a36Sopenharmony_ci * NOTE:  All requests here that have interface numbers as parameters
259462306a36Sopenharmony_ci * are assuming that somehow the configuration has been prevented from
259562306a36Sopenharmony_ci * changing.  But there's no mechanism to ensure that...
259662306a36Sopenharmony_ci */
259762306a36Sopenharmony_cistatic long usbdev_do_ioctl(struct file *file, unsigned int cmd,
259862306a36Sopenharmony_ci				void __user *p)
259962306a36Sopenharmony_ci{
260062306a36Sopenharmony_ci	struct usb_dev_state *ps = file->private_data;
260162306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
260262306a36Sopenharmony_ci	struct usb_device *dev = ps->dev;
260362306a36Sopenharmony_ci	int ret = -ENOTTY;
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	if (!(file->f_mode & FMODE_WRITE))
260662306a36Sopenharmony_ci		return -EPERM;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	usb_lock_device(dev);
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	/* Reap operations are allowed even after disconnection */
261162306a36Sopenharmony_ci	switch (cmd) {
261262306a36Sopenharmony_ci	case USBDEVFS_REAPURB:
261362306a36Sopenharmony_ci		snoop(&dev->dev, "%s: REAPURB\n", __func__);
261462306a36Sopenharmony_ci		ret = proc_reapurb(ps, p);
261562306a36Sopenharmony_ci		goto done;
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	case USBDEVFS_REAPURBNDELAY:
261862306a36Sopenharmony_ci		snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
261962306a36Sopenharmony_ci		ret = proc_reapurbnonblock(ps, p);
262062306a36Sopenharmony_ci		goto done;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
262362306a36Sopenharmony_ci	case USBDEVFS_REAPURB32:
262462306a36Sopenharmony_ci		snoop(&dev->dev, "%s: REAPURB32\n", __func__);
262562306a36Sopenharmony_ci		ret = proc_reapurb_compat(ps, p);
262662306a36Sopenharmony_ci		goto done;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	case USBDEVFS_REAPURBNDELAY32:
262962306a36Sopenharmony_ci		snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
263062306a36Sopenharmony_ci		ret = proc_reapurbnonblock_compat(ps, p);
263162306a36Sopenharmony_ci		goto done;
263262306a36Sopenharmony_ci#endif
263362306a36Sopenharmony_ci	}
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_ci	if (!connected(ps)) {
263662306a36Sopenharmony_ci		usb_unlock_device(dev);
263762306a36Sopenharmony_ci		return -ENODEV;
263862306a36Sopenharmony_ci	}
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	switch (cmd) {
264162306a36Sopenharmony_ci	case USBDEVFS_CONTROL:
264262306a36Sopenharmony_ci		snoop(&dev->dev, "%s: CONTROL\n", __func__);
264362306a36Sopenharmony_ci		ret = proc_control(ps, p);
264462306a36Sopenharmony_ci		if (ret >= 0)
264562306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
264662306a36Sopenharmony_ci		break;
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	case USBDEVFS_BULK:
264962306a36Sopenharmony_ci		snoop(&dev->dev, "%s: BULK\n", __func__);
265062306a36Sopenharmony_ci		ret = proc_bulk(ps, p);
265162306a36Sopenharmony_ci		if (ret >= 0)
265262306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
265362306a36Sopenharmony_ci		break;
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci	case USBDEVFS_RESETEP:
265662306a36Sopenharmony_ci		snoop(&dev->dev, "%s: RESETEP\n", __func__);
265762306a36Sopenharmony_ci		ret = proc_resetep(ps, p);
265862306a36Sopenharmony_ci		if (ret >= 0)
265962306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
266062306a36Sopenharmony_ci		break;
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	case USBDEVFS_RESET:
266362306a36Sopenharmony_ci		snoop(&dev->dev, "%s: RESET\n", __func__);
266462306a36Sopenharmony_ci		ret = proc_resetdevice(ps);
266562306a36Sopenharmony_ci		break;
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	case USBDEVFS_CLEAR_HALT:
266862306a36Sopenharmony_ci		snoop(&dev->dev, "%s: CLEAR_HALT\n", __func__);
266962306a36Sopenharmony_ci		ret = proc_clearhalt(ps, p);
267062306a36Sopenharmony_ci		if (ret >= 0)
267162306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
267262306a36Sopenharmony_ci		break;
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	case USBDEVFS_GETDRIVER:
267562306a36Sopenharmony_ci		snoop(&dev->dev, "%s: GETDRIVER\n", __func__);
267662306a36Sopenharmony_ci		ret = proc_getdriver(ps, p);
267762306a36Sopenharmony_ci		break;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	case USBDEVFS_CONNECTINFO:
268062306a36Sopenharmony_ci		snoop(&dev->dev, "%s: CONNECTINFO\n", __func__);
268162306a36Sopenharmony_ci		ret = proc_connectinfo(ps, p);
268262306a36Sopenharmony_ci		break;
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	case USBDEVFS_SETINTERFACE:
268562306a36Sopenharmony_ci		snoop(&dev->dev, "%s: SETINTERFACE\n", __func__);
268662306a36Sopenharmony_ci		ret = proc_setintf(ps, p);
268762306a36Sopenharmony_ci		break;
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	case USBDEVFS_SETCONFIGURATION:
269062306a36Sopenharmony_ci		snoop(&dev->dev, "%s: SETCONFIGURATION\n", __func__);
269162306a36Sopenharmony_ci		ret = proc_setconfig(ps, p);
269262306a36Sopenharmony_ci		break;
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci	case USBDEVFS_SUBMITURB:
269562306a36Sopenharmony_ci		snoop(&dev->dev, "%s: SUBMITURB\n", __func__);
269662306a36Sopenharmony_ci		ret = proc_submiturb(ps, p);
269762306a36Sopenharmony_ci		if (ret >= 0)
269862306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
269962306a36Sopenharmony_ci		break;
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
270262306a36Sopenharmony_ci	case USBDEVFS_CONTROL32:
270362306a36Sopenharmony_ci		snoop(&dev->dev, "%s: CONTROL32\n", __func__);
270462306a36Sopenharmony_ci		ret = proc_control_compat(ps, p);
270562306a36Sopenharmony_ci		if (ret >= 0)
270662306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
270762306a36Sopenharmony_ci		break;
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	case USBDEVFS_BULK32:
271062306a36Sopenharmony_ci		snoop(&dev->dev, "%s: BULK32\n", __func__);
271162306a36Sopenharmony_ci		ret = proc_bulk_compat(ps, p);
271262306a36Sopenharmony_ci		if (ret >= 0)
271362306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
271462306a36Sopenharmony_ci		break;
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	case USBDEVFS_DISCSIGNAL32:
271762306a36Sopenharmony_ci		snoop(&dev->dev, "%s: DISCSIGNAL32\n", __func__);
271862306a36Sopenharmony_ci		ret = proc_disconnectsignal_compat(ps, p);
271962306a36Sopenharmony_ci		break;
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	case USBDEVFS_SUBMITURB32:
272262306a36Sopenharmony_ci		snoop(&dev->dev, "%s: SUBMITURB32\n", __func__);
272362306a36Sopenharmony_ci		ret = proc_submiturb_compat(ps, p);
272462306a36Sopenharmony_ci		if (ret >= 0)
272562306a36Sopenharmony_ci			inode->i_mtime = inode_set_ctime_current(inode);
272662306a36Sopenharmony_ci		break;
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	case USBDEVFS_IOCTL32:
272962306a36Sopenharmony_ci		snoop(&dev->dev, "%s: IOCTL32\n", __func__);
273062306a36Sopenharmony_ci		ret = proc_ioctl_compat(ps, ptr_to_compat(p));
273162306a36Sopenharmony_ci		break;
273262306a36Sopenharmony_ci#endif
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	case USBDEVFS_DISCARDURB:
273562306a36Sopenharmony_ci		snoop(&dev->dev, "%s: DISCARDURB %px\n", __func__, p);
273662306a36Sopenharmony_ci		ret = proc_unlinkurb(ps, p);
273762306a36Sopenharmony_ci		break;
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci	case USBDEVFS_DISCSIGNAL:
274062306a36Sopenharmony_ci		snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
274162306a36Sopenharmony_ci		ret = proc_disconnectsignal(ps, p);
274262306a36Sopenharmony_ci		break;
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	case USBDEVFS_CLAIMINTERFACE:
274562306a36Sopenharmony_ci		snoop(&dev->dev, "%s: CLAIMINTERFACE\n", __func__);
274662306a36Sopenharmony_ci		ret = proc_claiminterface(ps, p);
274762306a36Sopenharmony_ci		break;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	case USBDEVFS_RELEASEINTERFACE:
275062306a36Sopenharmony_ci		snoop(&dev->dev, "%s: RELEASEINTERFACE\n", __func__);
275162306a36Sopenharmony_ci		ret = proc_releaseinterface(ps, p);
275262306a36Sopenharmony_ci		break;
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci	case USBDEVFS_IOCTL:
275562306a36Sopenharmony_ci		snoop(&dev->dev, "%s: IOCTL\n", __func__);
275662306a36Sopenharmony_ci		ret = proc_ioctl_default(ps, p);
275762306a36Sopenharmony_ci		break;
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	case USBDEVFS_CLAIM_PORT:
276062306a36Sopenharmony_ci		snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__);
276162306a36Sopenharmony_ci		ret = proc_claim_port(ps, p);
276262306a36Sopenharmony_ci		break;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	case USBDEVFS_RELEASE_PORT:
276562306a36Sopenharmony_ci		snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
276662306a36Sopenharmony_ci		ret = proc_release_port(ps, p);
276762306a36Sopenharmony_ci		break;
276862306a36Sopenharmony_ci	case USBDEVFS_GET_CAPABILITIES:
276962306a36Sopenharmony_ci		ret = proc_get_capabilities(ps, p);
277062306a36Sopenharmony_ci		break;
277162306a36Sopenharmony_ci	case USBDEVFS_DISCONNECT_CLAIM:
277262306a36Sopenharmony_ci		ret = proc_disconnect_claim(ps, p);
277362306a36Sopenharmony_ci		break;
277462306a36Sopenharmony_ci	case USBDEVFS_ALLOC_STREAMS:
277562306a36Sopenharmony_ci		ret = proc_alloc_streams(ps, p);
277662306a36Sopenharmony_ci		break;
277762306a36Sopenharmony_ci	case USBDEVFS_FREE_STREAMS:
277862306a36Sopenharmony_ci		ret = proc_free_streams(ps, p);
277962306a36Sopenharmony_ci		break;
278062306a36Sopenharmony_ci	case USBDEVFS_DROP_PRIVILEGES:
278162306a36Sopenharmony_ci		ret = proc_drop_privileges(ps, p);
278262306a36Sopenharmony_ci		break;
278362306a36Sopenharmony_ci	case USBDEVFS_GET_SPEED:
278462306a36Sopenharmony_ci		ret = ps->dev->speed;
278562306a36Sopenharmony_ci		break;
278662306a36Sopenharmony_ci	case USBDEVFS_FORBID_SUSPEND:
278762306a36Sopenharmony_ci		ret = proc_forbid_suspend(ps);
278862306a36Sopenharmony_ci		break;
278962306a36Sopenharmony_ci	case USBDEVFS_ALLOW_SUSPEND:
279062306a36Sopenharmony_ci		ret = proc_allow_suspend(ps);
279162306a36Sopenharmony_ci		break;
279262306a36Sopenharmony_ci	case USBDEVFS_WAIT_FOR_RESUME:
279362306a36Sopenharmony_ci		ret = proc_wait_for_resume(ps);
279462306a36Sopenharmony_ci		break;
279562306a36Sopenharmony_ci	}
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	/* Handle variable-length commands */
279862306a36Sopenharmony_ci	switch (cmd & ~IOCSIZE_MASK) {
279962306a36Sopenharmony_ci	case USBDEVFS_CONNINFO_EX(0):
280062306a36Sopenharmony_ci		ret = proc_conninfo_ex(ps, p, _IOC_SIZE(cmd));
280162306a36Sopenharmony_ci		break;
280262306a36Sopenharmony_ci	}
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci done:
280562306a36Sopenharmony_ci	usb_unlock_device(dev);
280662306a36Sopenharmony_ci	if (ret >= 0)
280762306a36Sopenharmony_ci		inode->i_atime = current_time(inode);
280862306a36Sopenharmony_ci	return ret;
280962306a36Sopenharmony_ci}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_cistatic long usbdev_ioctl(struct file *file, unsigned int cmd,
281262306a36Sopenharmony_ci			unsigned long arg)
281362306a36Sopenharmony_ci{
281462306a36Sopenharmony_ci	int ret;
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	ret = usbdev_do_ioctl(file, cmd, (void __user *)arg);
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	return ret;
281962306a36Sopenharmony_ci}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci/* No kernel lock - fine */
282262306a36Sopenharmony_cistatic __poll_t usbdev_poll(struct file *file,
282362306a36Sopenharmony_ci				struct poll_table_struct *wait)
282462306a36Sopenharmony_ci{
282562306a36Sopenharmony_ci	struct usb_dev_state *ps = file->private_data;
282662306a36Sopenharmony_ci	__poll_t mask = 0;
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	poll_wait(file, &ps->wait, wait);
282962306a36Sopenharmony_ci	if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
283062306a36Sopenharmony_ci		mask |= EPOLLOUT | EPOLLWRNORM;
283162306a36Sopenharmony_ci	if (!connected(ps))
283262306a36Sopenharmony_ci		mask |= EPOLLHUP;
283362306a36Sopenharmony_ci	if (list_empty(&ps->list))
283462306a36Sopenharmony_ci		mask |= EPOLLERR;
283562306a36Sopenharmony_ci	return mask;
283662306a36Sopenharmony_ci}
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ciconst struct file_operations usbdev_file_operations = {
283962306a36Sopenharmony_ci	.owner =	  THIS_MODULE,
284062306a36Sopenharmony_ci	.llseek =	  no_seek_end_llseek,
284162306a36Sopenharmony_ci	.read =		  usbdev_read,
284262306a36Sopenharmony_ci	.poll =		  usbdev_poll,
284362306a36Sopenharmony_ci	.unlocked_ioctl = usbdev_ioctl,
284462306a36Sopenharmony_ci	.compat_ioctl =   compat_ptr_ioctl,
284562306a36Sopenharmony_ci	.mmap =           usbdev_mmap,
284662306a36Sopenharmony_ci	.open =		  usbdev_open,
284762306a36Sopenharmony_ci	.release =	  usbdev_release,
284862306a36Sopenharmony_ci};
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_cistatic void usbdev_remove(struct usb_device *udev)
285162306a36Sopenharmony_ci{
285262306a36Sopenharmony_ci	struct usb_dev_state *ps;
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	/* Protect against simultaneous resume */
285562306a36Sopenharmony_ci	mutex_lock(&usbfs_mutex);
285662306a36Sopenharmony_ci	while (!list_empty(&udev->filelist)) {
285762306a36Sopenharmony_ci		ps = list_entry(udev->filelist.next, struct usb_dev_state, list);
285862306a36Sopenharmony_ci		destroy_all_async(ps);
285962306a36Sopenharmony_ci		wake_up_all(&ps->wait);
286062306a36Sopenharmony_ci		WRITE_ONCE(ps->not_yet_resumed, 0);
286162306a36Sopenharmony_ci		wake_up_all(&ps->wait_for_resume);
286262306a36Sopenharmony_ci		list_del_init(&ps->list);
286362306a36Sopenharmony_ci		if (ps->discsignr)
286462306a36Sopenharmony_ci			kill_pid_usb_asyncio(ps->discsignr, EPIPE, ps->disccontext,
286562306a36Sopenharmony_ci					     ps->disc_pid, ps->cred);
286662306a36Sopenharmony_ci	}
286762306a36Sopenharmony_ci	mutex_unlock(&usbfs_mutex);
286862306a36Sopenharmony_ci}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_cistatic int usbdev_notify(struct notifier_block *self,
287162306a36Sopenharmony_ci			       unsigned long action, void *dev)
287262306a36Sopenharmony_ci{
287362306a36Sopenharmony_ci	switch (action) {
287462306a36Sopenharmony_ci	case USB_DEVICE_ADD:
287562306a36Sopenharmony_ci		break;
287662306a36Sopenharmony_ci	case USB_DEVICE_REMOVE:
287762306a36Sopenharmony_ci		usbdev_remove(dev);
287862306a36Sopenharmony_ci		break;
287962306a36Sopenharmony_ci	}
288062306a36Sopenharmony_ci	return NOTIFY_OK;
288162306a36Sopenharmony_ci}
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_cistatic struct notifier_block usbdev_nb = {
288462306a36Sopenharmony_ci	.notifier_call =	usbdev_notify,
288562306a36Sopenharmony_ci};
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_cistatic struct cdev usb_device_cdev;
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ciint __init usb_devio_init(void)
289062306a36Sopenharmony_ci{
289162306a36Sopenharmony_ci	int retval;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
289462306a36Sopenharmony_ci					"usb_device");
289562306a36Sopenharmony_ci	if (retval) {
289662306a36Sopenharmony_ci		printk(KERN_ERR "Unable to register minors for usb_device\n");
289762306a36Sopenharmony_ci		goto out;
289862306a36Sopenharmony_ci	}
289962306a36Sopenharmony_ci	cdev_init(&usb_device_cdev, &usbdev_file_operations);
290062306a36Sopenharmony_ci	retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
290162306a36Sopenharmony_ci	if (retval) {
290262306a36Sopenharmony_ci		printk(KERN_ERR "Unable to get usb_device major %d\n",
290362306a36Sopenharmony_ci		       USB_DEVICE_MAJOR);
290462306a36Sopenharmony_ci		goto error_cdev;
290562306a36Sopenharmony_ci	}
290662306a36Sopenharmony_ci	usb_register_notify(&usbdev_nb);
290762306a36Sopenharmony_ciout:
290862306a36Sopenharmony_ci	return retval;
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_cierror_cdev:
291162306a36Sopenharmony_ci	unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
291262306a36Sopenharmony_ci	goto out;
291362306a36Sopenharmony_ci}
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_civoid usb_devio_cleanup(void)
291662306a36Sopenharmony_ci{
291762306a36Sopenharmony_ci	usb_unregister_notify(&usbdev_nb);
291862306a36Sopenharmony_ci	cdev_del(&usb_device_cdev);
291962306a36Sopenharmony_ci	unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
292062306a36Sopenharmony_ci}
2921