2005-04-17 06:20:36 +08:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* devio.c -- User space communication with USB devices.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1999-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*
|
|
|
|
* This file implements the usbfs/x/y files, where
|
|
|
|
* x is the bus number and y the device number.
|
|
|
|
*
|
|
|
|
* It allows user space programs/"drivers" to communicate directly
|
|
|
|
* with USB devices without intervening kernel driver.
|
|
|
|
*
|
|
|
|
* Revision history
|
|
|
|
* 22.12.1999 0.1 Initial release (split from proc_usb.c)
|
|
|
|
* 04.01.2000 0.2 Turned into its own filesystem
|
2005-10-11 01:44:29 +08:00
|
|
|
* 30.09.2005 0.3 Fix user-triggerable oops in async URB delivery
|
|
|
|
* (CAN-2005-3055)
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/signal.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/module.h>
|
2013-02-02 15:57:53 +08:00
|
|
|
#include <linux/string.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/usb.h>
|
|
|
|
#include <linux/usbdevice_fs.h>
|
2010-04-25 05:21:52 +08:00
|
|
|
#include <linux/usb/hcd.h> /* for usbcore internals */
|
2005-07-31 07:05:53 +08:00
|
|
|
#include <linux/cdev.h>
|
2005-06-21 12:15:16 +08:00
|
|
|
#include <linux/notifier.h>
|
2006-06-30 16:55:48 +08:00
|
|
|
#include <linux/security.h>
|
2011-09-26 23:45:18 +08:00
|
|
|
#include <linux/user_namespace.h>
|
2012-07-04 15:18:03 +08:00
|
|
|
#include <linux/scatterlist.h>
|
2013-05-17 15:12:34 +08:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/byteorder.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
|
|
|
|
#include "usb.h"
|
|
|
|
|
2005-07-31 07:05:53 +08:00
|
|
|
#define USB_MAXBUS 64
|
2013-05-17 15:13:24 +08:00
|
|
|
#define USB_DEVICE_MAX (USB_MAXBUS * 128)
|
2012-07-04 15:18:03 +08:00
|
|
|
#define USB_SG_SIZE 16384 /* split-size for large txs */
|
2005-07-31 07:05:53 +08:00
|
|
|
|
usbfs: private mutex for open, release, and remove
The usbfs code doesn't provide sufficient mutual exclusion among open,
release, and remove. Release vs. remove is okay because they both
acquire the device lock, but open is not exclusive with either one. All
three routines modify the udev->filelist linked list, so they must not
run concurrently.
Apparently someone gave this a minimum amount of thought in the past by
explicitly acquiring the BKL at the start of the usbdev_open routine.
Oddly enough, there's a comment pointing out that locking is unnecessary
because chrdev_open already has acquired the BKL.
But this ignores the point that the files in /proc/bus/usb/* are not
char device files; they are regular files and so they don't get any
special locking. Furthermore it's necessary to acquire the same lock in
the release and remove routines, which the code does not do.
Yet another problem arises because the same file_operations structure is
accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file
nodes. Even when one of them has been removed, it's still possible for
userspace to open the other. So simple locking around the individual
remove routines is insufficient; we need to lock the entire
usb_notify_remove_device notifier chain.
Rather than rely on the BKL, this patch (as723) introduces a new private
mutex for the purpose. Holding the BKL while invoking a notifier chain
doesn't seem like a good idea.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-07-02 10:05:01 +08:00
|
|
|
/* Mutual exclusion for removal, open, and release */
|
|
|
|
DEFINE_MUTEX(usbfs_mutex);
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state {
|
2008-06-25 02:47:04 +08:00
|
|
|
struct list_head list; /* state list */
|
|
|
|
struct usb_device *dev;
|
|
|
|
struct file *file;
|
|
|
|
spinlock_t lock; /* protects the async urb lists */
|
|
|
|
struct list_head async_pending;
|
|
|
|
struct list_head async_completed;
|
|
|
|
wait_queue_head_t wait; /* wake up if a request completed */
|
|
|
|
unsigned int discsignr;
|
|
|
|
struct pid *disc_pid;
|
2011-09-26 23:45:18 +08:00
|
|
|
const struct cred *cred;
|
2008-06-25 02:47:04 +08:00
|
|
|
void __user *disccontext;
|
|
|
|
unsigned long ifclaimed;
|
|
|
|
u32 secid;
|
2009-09-01 23:09:56 +08:00
|
|
|
u32 disabled_bulk_eps;
|
2008-06-25 02:47:04 +08:00
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct async {
|
|
|
|
struct list_head asynclist;
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps;
|
2006-10-02 17:17:28 +08:00
|
|
|
struct pid *pid;
|
2011-09-26 23:45:18 +08:00
|
|
|
const struct cred *cred;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int signr;
|
|
|
|
unsigned int ifnum;
|
|
|
|
void __user *userbuffer;
|
|
|
|
void __user *userurb;
|
|
|
|
struct urb *urb;
|
2011-11-18 05:41:25 +08:00
|
|
|
unsigned int mem_usage;
|
2007-08-25 03:42:52 +08:00
|
|
|
int status;
|
2006-06-30 16:55:48 +08:00
|
|
|
u32 secid;
|
2009-09-01 23:09:56 +08:00
|
|
|
u8 bulk_addr;
|
|
|
|
u8 bulk_status;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2012-01-13 07:02:20 +08:00
|
|
|
static bool usbfs_snoop;
|
2008-01-31 07:21:33 +08:00
|
|
|
module_param(usbfs_snoop, bool, S_IRUGO | S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define snoop(dev, format, arg...) \
|
|
|
|
do { \
|
|
|
|
if (usbfs_snoop) \
|
2008-01-31 07:21:33 +08:00
|
|
|
dev_info(dev , format , ## arg); \
|
2005-04-17 06:20:36 +08:00
|
|
|
} while (0)
|
|
|
|
|
2009-06-29 23:02:04 +08:00
|
|
|
enum snoop_when {
|
|
|
|
SUBMIT, COMPLETE
|
|
|
|
};
|
2005-08-11 03:15:57 +08:00
|
|
|
|
2009-06-29 23:02:04 +08:00
|
|
|
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-18 05:41:25 +08:00
|
|
|
/* Limit on the total amount of memory we can allocate for transfers */
|
2011-11-18 05:41:35 +08:00
|
|
|
static unsigned usbfs_memory_mb = 16;
|
|
|
|
module_param(usbfs_memory_mb, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(usbfs_memory_mb,
|
|
|
|
"maximum MB allowed for usbfs buffers (0 = no limit)");
|
|
|
|
|
2014-01-04 13:54:41 +08:00
|
|
|
/* Hard limit, necessary to avoid arithmetic overflow */
|
2011-11-18 05:41:35 +08:00
|
|
|
#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-18 05:41:25 +08:00
|
|
|
static atomic_t usbfs_memory_usage; /* Total memory currently allocated */
|
|
|
|
|
|
|
|
/* Check whether it's okay to allocate more memory for a transfer */
|
|
|
|
static int usbfs_increase_memory_usage(unsigned amount)
|
|
|
|
{
|
2011-11-18 05:41:35 +08:00
|
|
|
unsigned lim;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert usbfs_memory_mb to bytes, avoiding overflows.
|
|
|
|
* 0 means use the hard limit (effectively unlimited).
|
|
|
|
*/
|
|
|
|
lim = ACCESS_ONCE(usbfs_memory_mb);
|
|
|
|
if (lim == 0 || lim > (USBFS_XFER_MAX >> 20))
|
|
|
|
lim = USBFS_XFER_MAX;
|
|
|
|
else
|
|
|
|
lim <<= 20;
|
|
|
|
|
2011-11-18 05:41:25 +08:00
|
|
|
atomic_add(amount, &usbfs_memory_usage);
|
2011-11-18 05:41:35 +08:00
|
|
|
if (atomic_read(&usbfs_memory_usage) <= lim)
|
2011-11-18 05:41:25 +08:00
|
|
|
return 0;
|
|
|
|
atomic_sub(amount, &usbfs_memory_usage);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Memory for a transfer is being deallocated */
|
|
|
|
static void usbfs_decrease_memory_usage(unsigned amount)
|
|
|
|
{
|
|
|
|
atomic_sub(amount, &usbfs_memory_usage);
|
|
|
|
}
|
2009-06-29 23:02:04 +08:00
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int connected(struct usb_dev_state *ps)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-07-02 10:05:56 +08:00
|
|
|
return (!list_empty(&ps->list) &&
|
|
|
|
ps->dev->state != USB_STATE_NOTATTACHED);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
|
|
|
|
{
|
|
|
|
loff_t ret;
|
|
|
|
|
2013-01-24 06:07:38 +08:00
|
|
|
mutex_lock(&file_inode(file)->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (orig) {
|
|
|
|
case 0:
|
|
|
|
file->f_pos = offset;
|
|
|
|
ret = file->f_pos;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
file->f_pos += offset;
|
|
|
|
ret = file->f_pos;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
default:
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
2013-01-24 06:07:38 +08:00
|
|
|
mutex_unlock(&file_inode(file)->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
|
loff_t *ppos)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = file->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct usb_device *dev = ps->dev;
|
|
|
|
ssize_t ret = 0;
|
|
|
|
unsigned len;
|
|
|
|
loff_t pos;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pos = *ppos;
|
|
|
|
usb_lock_device(dev);
|
2006-07-02 10:05:56 +08:00
|
|
|
if (!connected(ps)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto err;
|
|
|
|
} else if (pos < 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pos < sizeof(struct usb_device_descriptor)) {
|
2008-01-31 07:21:33 +08:00
|
|
|
/* 18 bytes - fits on the stack */
|
|
|
|
struct usb_device_descriptor temp_desc;
|
2006-01-07 04:24:25 +08:00
|
|
|
|
|
|
|
memcpy(&temp_desc, &dev->descriptor, sizeof(dev->descriptor));
|
2006-01-19 15:55:07 +08:00
|
|
|
le16_to_cpus(&temp_desc.bcdUSB);
|
|
|
|
le16_to_cpus(&temp_desc.idVendor);
|
|
|
|
le16_to_cpus(&temp_desc.idProduct);
|
|
|
|
le16_to_cpus(&temp_desc.bcdDevice);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
len = sizeof(struct usb_device_descriptor) - pos;
|
|
|
|
if (len > nbytes)
|
|
|
|
len = nbytes;
|
2006-01-07 04:24:25 +08:00
|
|
|
if (copy_to_user(buf, ((char *)&temp_desc) + pos, len)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ppos += len;
|
|
|
|
buf += len;
|
|
|
|
nbytes -= len;
|
|
|
|
ret += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = sizeof(struct usb_device_descriptor);
|
|
|
|
for (i = 0; nbytes && i < dev->descriptor.bNumConfigurations; i++) {
|
|
|
|
struct usb_config_descriptor *config =
|
|
|
|
(struct usb_config_descriptor *)dev->rawdescriptors[i];
|
|
|
|
unsigned int length = le16_to_cpu(config->wTotalLength);
|
|
|
|
|
|
|
|
if (*ppos < pos + length) {
|
|
|
|
|
|
|
|
/* The descriptor may claim to be longer than it
|
|
|
|
* really is. Here is the actual allocated length. */
|
|
|
|
unsigned alloclen =
|
|
|
|
le16_to_cpu(dev->config[i].desc.wTotalLength);
|
|
|
|
|
|
|
|
len = length - (*ppos - pos);
|
|
|
|
if (len > nbytes)
|
|
|
|
len = nbytes;
|
|
|
|
|
|
|
|
/* Simply don't write (skip over) unallocated parts */
|
|
|
|
if (alloclen > (*ppos - pos)) {
|
|
|
|
alloclen -= (*ppos - pos);
|
|
|
|
if (copy_to_user(buf,
|
|
|
|
dev->rawdescriptors[i] + (*ppos - pos),
|
|
|
|
min(len, alloclen))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ppos += len;
|
|
|
|
buf += len;
|
|
|
|
nbytes -= len;
|
|
|
|
ret += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos += length;
|
|
|
|
}
|
|
|
|
|
|
|
|
err:
|
|
|
|
usb_unlock_device(dev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* async list handling
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct async *alloc_async(unsigned int numisoframes)
|
|
|
|
{
|
2008-01-05 18:01:07 +08:00
|
|
|
struct async *as;
|
2005-10-25 03:38:24 +08:00
|
|
|
|
2008-01-05 18:01:07 +08:00
|
|
|
as = kzalloc(sizeof(struct async), GFP_KERNEL);
|
2008-01-31 07:21:33 +08:00
|
|
|
if (!as)
|
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
|
|
|
|
if (!as->urb) {
|
|
|
|
kfree(as);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-01-31 07:21:33 +08:00
|
|
|
return as;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void free_async(struct async *as)
|
|
|
|
{
|
2012-07-04 15:18:03 +08:00
|
|
|
int i;
|
|
|
|
|
2006-10-02 17:17:28 +08:00
|
|
|
put_pid(as->pid);
|
usbfs: Fix oops related to user namespace conversion.
When running the Point Grey "flycap" program for their USB 3.0 camera
(which was running as a USB 2.0 device for some reason), I trigger this
oops whenever I try to open a video stream:
Dec 15 16:48:34 puck kernel: [ 1798.715559] BUG: unable to handle kernel NULL pointer dereference at (null)
Dec 15 16:48:34 puck kernel: [ 1798.719153] IP: [<ffffffff8147841e>] free_async+0x1e/0x70
Dec 15 16:48:34 puck kernel: [ 1798.720991] PGD 6f833067 PUD 6fc56067 PMD 0
Dec 15 16:48:34 puck kernel: [ 1798.722815] Oops: 0002 [#1] SMP
Dec 15 16:48:34 puck kernel: [ 1798.724627] CPU 0
Dec 15 16:48:34 puck kernel: [ 1798.724636] Modules linked in: ecryptfs encrypted_keys sha1_generic trusted binfmt_misc sha256_generic aesni_intel cryptd aes_x86_64 aes_generic parport_pc dm_crypt ppdev joydev snd_hda_codec_hdmi snd_hda_codec_conexant arc4 iwlwifi snd_hda_intel snd_hda_codec snd_hwdep snd_pcm thinkpad_acpi mac80211 snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq snd_timer btusb uvcvideo snd_seq_device bluetooth videodev psmouse snd v4l2_compat_ioctl32 serio_raw tpm_tis cfg80211 tpm tpm_bios nvram soundcore snd_page_alloc lp parport i915 xhci_hcd ahci libahci drm_kms_helper drm sdhci_pci sdhci e1000e i2c_algo_bit video
Dec 15 16:48:34 puck kernel: [ 1798.734212]
Dec 15 16:48:34 puck kernel: [ 1798.736162] Pid: 2713, comm: FlyCap2 Not tainted 3.2.0-rc5+ #28 LENOVO 4286CTO/4286CTO
Dec 15 16:48:34 puck kernel: [ 1798.738148] RIP: 0010:[<ffffffff8147841e>] [<ffffffff8147841e>] free_async+0x1e/0x70
Dec 15 16:48:34 puck kernel: [ 1798.740134] RSP: 0018:ffff88005715fd78 EFLAGS: 00010296
Dec 15 16:48:34 puck kernel: [ 1798.742118] RAX: 00000000fffffff4 RBX: ffff88006fe8f900 RCX: 0000000000004118
Dec 15 16:48:34 puck kernel: [ 1798.744116] RDX: 0000000001000000 RSI: 0000000000016390 RDI: 0000000000000000
Dec 15 16:48:34 puck kernel: [ 1798.746087] RBP: ffff88005715fd88 R08: 0000000000000000 R09: ffffffff8146f22e
Dec 15 16:48:34 puck kernel: [ 1798.748018] R10: ffff88006e520ac0 R11: 0000000000000001 R12: ffff88005715fe28
Dec 15 16:48:34 puck kernel: [ 1798.749916] R13: ffff88005d31df00 R14: ffff88006fe8f900 R15: 00007f688c995cb8
Dec 15 16:48:34 puck kernel: [ 1798.751785] FS: 00007f68a366da40(0000) GS:ffff880100200000(0000) knlGS:0000000000000000
Dec 15 16:48:34 puck kernel: [ 1798.753659] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Dec 15 16:48:34 puck kernel: [ 1798.755509] CR2: 0000000000000000 CR3: 00000000706bb000 CR4: 00000000000406f0
Dec 15 16:48:34 puck kernel: [ 1798.757334] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
Dec 15 16:48:34 puck kernel: [ 1798.759124] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Dec 15 16:48:34 puck kernel: [ 1798.760871] Process FlyCap2 (pid: 2713, threadinfo ffff88005715e000, task ffff88006c675b80)
Dec 15 16:48:34 puck kernel: [ 1798.762605] Stack:
Dec 15 16:48:34 puck kernel: [ 1798.764297] ffff88005715fe28 0000000000000000 ffff88005715fe08 ffffffff81479058
Dec 15 16:48:34 puck kernel: [ 1798.766020] 0000000000000000 ffffea0000004000 ffff880000004118 0000000000000000
Dec 15 16:48:34 puck kernel: [ 1798.767750] ffff880000000001 ffff88006e520ac0 fffffff46fd81180 0000000000000000
Dec 15 16:48:34 puck kernel: [ 1798.769472] Call Trace:
Dec 15 16:48:34 puck kernel: [ 1798.771147] [<ffffffff81479058>] proc_do_submiturb+0x778/0xa00
Dec 15 16:48:34 puck kernel: [ 1798.772798] [<ffffffff8147a5fd>] usbdev_do_ioctl+0x24d/0x1200
Dec 15 16:48:34 puck kernel: [ 1798.774410] [<ffffffff8147b5de>] usbdev_ioctl+0xe/0x20
Dec 15 16:48:34 puck kernel: [ 1798.775975] [<ffffffff81189259>] do_vfs_ioctl+0x99/0x600
Dec 15 16:48:34 puck kernel: [ 1798.777534] [<ffffffff81189851>] sys_ioctl+0x91/0xa0
Dec 15 16:48:34 puck kernel: [ 1798.779088] [<ffffffff816247c2>] system_call_fastpath+0x16/0x1b
ec 15 16:48:34 puck kernel: [ 1798.780634] Code: 51 ff ff ff e9 29 ff ff ff 0f 1f 40 00 55 48 89 e5 53 48 83 ec 08 66 66 66 66 90 48 89 fb 48 8b 7f 18 e8 a6 ea c0 ff 4
8 8b 7b 20 <f0> ff 0f 0f 94 c0 84 c0 74 05 e8 d3 99 c1 ff 48 8b 43 40 48 8b
Dec 15 16:48:34 puck kernel: [ 1798.783970] RIP [<ffffffff8147841e>] free_async+0x1e/0x70
Dec 15 16:48:34 puck kernel: [ 1798.785630] RSP <ffff88005715fd78>
Dec 15 16:48:34 puck kernel: [ 1798.787274] CR2: 0000000000000000
Dec 15 16:48:34 puck kernel: [ 1798.794728] ---[ end trace 52894d3355f88d19 ]---
markup_oops.pl says the oops is in put_cred:
ffffffff81478401: 48 89 e5 mov %rsp,%rbp
ffffffff81478404: 53 push %rbx
ffffffff81478405: 48 83 ec 08 sub $0x8,%rsp
ffffffff81478409: e8 f2 c0 1a 00 callq ffffffff81624500 <mcount>
ffffffff8147840e: 48 89 fb mov %rdi,%rbx | %ebx => ffff88006fe8f900
put_pid(as->pid);
ffffffff81478411: 48 8b 7f 18 mov 0x18(%rdi),%rdi
ffffffff81478415: e8 a6 ea c0 ff callq ffffffff81086ec0 <put_pid>
put_cred(as->cred);
ffffffff8147841a: 48 8b 7b 20 mov 0x20(%rbx),%rdi | %edi => 0 %ebx = ffff88006fe8f900
*/
static inline int atomic_dec_and_test(atomic_t *v)
{
unsigned char c;
asm volatile(LOCK_PREFIX "decl %0; sete %1"
*ffffffff8147841e: f0 ff 0f lock decl (%rdi) | %edi = 0 <--- faulting instruction
ffffffff81478421: 0f 94 c0 sete %al
static inline void put_cred(const struct cred *_cred)
{
struct cred *cred = (struct cred *) _cred;
validate_creds(cred);
if (atomic_dec_and_test(&(cred)->usage))
ffffffff81478424: 84 c0 test %al,%al
ffffffff81478426: 74 05 je ffffffff8147842d <free_async+0x2d>
__put_cred(cred);
ffffffff81478428: e8 d3 99 c1 ff callq ffffffff81091e00 <__put_cred>
kfree(as->urb->transfer_buffer);
ffffffff8147842d: 48 8b 43 40 mov 0x40(%rbx),%rax
ffffffff81478431: 48 8b 78 68 mov 0x68(%rax),%rdi
ffffffff81478435: e8 a6 e1 ce ff callq ffffffff811665e0 <kfree>
kfree(as->urb->setup_packet);
ffffffff8147843a: 48 8b 43 40 mov 0x40(%rbx),%rax
ffffffff8147843e: 48 8b b8 90 00 00 00 mov 0x90(%rax),%rdi
ffffffff81478445: e8 96 e1 ce ff callq ffffffff811665e0 <kfree>
usb_free_urb(as->urb);
ffffffff8147844a: 48 8b 7b 40 mov 0x40(%rbx),%rdi
ffffffff8147844e: e8 0d 6b ff ff callq ffffffff8146ef60 <usb_free_urb>
This bug seems to have been introduced by commit
d178bc3a708f39cbfefc3fab37032d3f2511b4ec "user namespace: usb: make usb
urbs user namespace aware (v2)"
I'm not sure if this is right fix, but it does stop the oops.
Unfortunately, the Point Grey software still refuses to work, but it's a
closed source app, so I can't fix it.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-12-17 03:26:30 +08:00
|
|
|
if (as->cred)
|
|
|
|
put_cred(as->cred);
|
2012-07-04 15:18:03 +08:00
|
|
|
for (i = 0; i < as->urb->num_sgs; i++) {
|
|
|
|
if (sg_page(&as->urb->sg[i]))
|
|
|
|
kfree(sg_virt(&as->urb->sg[i]));
|
|
|
|
}
|
|
|
|
kfree(as->urb->sg);
|
2005-04-19 08:39:33 +08:00
|
|
|
kfree(as->urb->transfer_buffer);
|
|
|
|
kfree(as->urb->setup_packet);
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_free_urb(as->urb);
|
2011-11-18 05:41:25 +08:00
|
|
|
usbfs_decrease_memory_usage(as->mem_usage);
|
2005-04-19 08:39:33 +08:00
|
|
|
kfree(as);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-03-10 01:44:48 +08:00
|
|
|
static void async_newpending(struct async *as)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = as->ps;
|
2008-01-31 07:21:33 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ps->lock, flags);
|
|
|
|
list_add_tail(&as->asynclist, &ps->async_pending);
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-03-10 01:44:48 +08:00
|
|
|
static void async_removepending(struct async *as)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = as->ps;
|
2008-01-31 07:21:33 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ps->lock, flags);
|
|
|
|
list_del_init(&as->asynclist);
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static struct async *async_getcompleted(struct usb_dev_state *ps)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-01-31 07:21:33 +08:00
|
|
|
unsigned long flags;
|
|
|
|
struct async *as = NULL;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ps->lock, flags);
|
|
|
|
if (!list_empty(&ps->async_completed)) {
|
|
|
|
as = list_entry(ps->async_completed.next, struct async,
|
|
|
|
asynclist);
|
|
|
|
list_del_init(&as->asynclist);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
|
|
|
return as;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static struct async *async_getpending(struct usb_dev_state *ps,
|
2008-01-31 07:21:33 +08:00
|
|
|
void __user *userurb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-01-31 07:21:33 +08:00
|
|
|
struct async *as;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
list_for_each_entry(as, &ps->async_pending, asynclist)
|
|
|
|
if (as->userurb == userurb) {
|
|
|
|
list_del_init(&as->asynclist);
|
|
|
|
return as;
|
|
|
|
}
|
2012-05-18 20:12:51 +08:00
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-06-29 23:02:04 +08:00
|
|
|
static void snoop_urb(struct usb_device *udev,
|
|
|
|
void __user *userurb, int pipe, unsigned length,
|
2010-01-27 06:07:29 +08:00
|
|
|
int timeout_or_status, enum snoop_when when,
|
|
|
|
unsigned char *data, unsigned data_len)
|
2005-06-21 12:15:16 +08:00
|
|
|
{
|
2009-06-29 23:02:04 +08:00
|
|
|
static const char *types[] = {"isoc", "int", "ctrl", "bulk"};
|
|
|
|
static const char *dirs[] = {"out", "in"};
|
|
|
|
int ep;
|
|
|
|
const char *t, *d;
|
2005-06-21 12:15:16 +08:00
|
|
|
|
|
|
|
if (!usbfs_snoop)
|
|
|
|
return;
|
|
|
|
|
2009-06-29 23:02:04 +08:00
|
|
|
ep = usb_pipeendpoint(pipe);
|
|
|
|
t = types[usb_pipetype(pipe)];
|
|
|
|
d = dirs[!!usb_pipein(pipe)];
|
|
|
|
|
|
|
|
if (userurb) { /* Async */
|
|
|
|
if (when == SUBMIT)
|
|
|
|
dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
|
|
|
|
"length %u\n",
|
|
|
|
userurb, ep, t, d, length);
|
|
|
|
else
|
|
|
|
dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
|
|
|
|
"actual_length %u status %d\n",
|
|
|
|
userurb, ep, t, d, length,
|
|
|
|
timeout_or_status);
|
|
|
|
} else {
|
|
|
|
if (when == SUBMIT)
|
|
|
|
dev_info(&udev->dev, "ep%d %s-%s, length %u, "
|
|
|
|
"timeout %d\n",
|
|
|
|
ep, t, d, length, timeout_or_status);
|
|
|
|
else
|
|
|
|
dev_info(&udev->dev, "ep%d %s-%s, actual_length %u, "
|
|
|
|
"status %d\n",
|
|
|
|
ep, t, d, length, timeout_or_status);
|
|
|
|
}
|
2010-01-27 06:07:29 +08:00
|
|
|
|
|
|
|
if (data && data_len > 0) {
|
|
|
|
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
|
|
|
|
data, data_len, 1);
|
|
|
|
}
|
2005-06-21 12:15:16 +08:00
|
|
|
}
|
|
|
|
|
2012-07-04 15:18:03 +08:00
|
|
|
static void snoop_urb_data(struct urb *urb, unsigned len)
|
|
|
|
{
|
|
|
|
int i, size;
|
|
|
|
|
|
|
|
if (!usbfs_snoop)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (urb->num_sgs == 0) {
|
|
|
|
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
|
|
|
|
urb->transfer_buffer, len, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < urb->num_sgs && len; i++) {
|
|
|
|
size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
|
|
|
|
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
|
|
|
|
sg_virt(&urb->sg[i]), size, 1);
|
|
|
|
len -= size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb)
|
|
|
|
{
|
|
|
|
unsigned i, len, size;
|
|
|
|
|
|
|
|
if (urb->number_of_packets > 0) /* Isochronous */
|
|
|
|
len = urb->transfer_buffer_length;
|
|
|
|
else /* Non-Isoc */
|
|
|
|
len = urb->actual_length;
|
|
|
|
|
|
|
|
if (urb->num_sgs == 0) {
|
|
|
|
if (copy_to_user(userbuffer, urb->transfer_buffer, len))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < urb->num_sgs && len; i++) {
|
|
|
|
size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
|
|
|
|
if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size))
|
|
|
|
return -EFAULT;
|
|
|
|
userbuffer += size;
|
|
|
|
len -= size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-01 23:09:56 +08:00
|
|
|
#define AS_CONTINUATION 1
|
|
|
|
#define AS_UNLINK 2
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static void cancel_bulk_urbs(struct usb_dev_state *ps, unsigned bulk_addr)
|
2009-09-01 23:09:56 +08:00
|
|
|
__releases(ps->lock)
|
|
|
|
__acquires(ps->lock)
|
|
|
|
{
|
2012-05-18 20:12:51 +08:00
|
|
|
struct urb *urb;
|
2009-09-01 23:09:56 +08:00
|
|
|
struct async *as;
|
|
|
|
|
|
|
|
/* Mark all the pending URBs that match bulk_addr, up to but not
|
|
|
|
* including the first one without AS_CONTINUATION. If such an
|
|
|
|
* URB is encountered then a new transfer has already started so
|
|
|
|
* the endpoint doesn't need to be disabled; otherwise it does.
|
|
|
|
*/
|
|
|
|
list_for_each_entry(as, &ps->async_pending, asynclist) {
|
|
|
|
if (as->bulk_addr == bulk_addr) {
|
|
|
|
if (as->bulk_status != AS_CONTINUATION)
|
|
|
|
goto rescan;
|
|
|
|
as->bulk_status = AS_UNLINK;
|
|
|
|
as->bulk_addr = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ps->disabled_bulk_eps |= (1 << bulk_addr);
|
|
|
|
|
|
|
|
/* Now carefully unlink all the marked pending URBs */
|
|
|
|
rescan:
|
|
|
|
list_for_each_entry(as, &ps->async_pending, asynclist) {
|
|
|
|
if (as->bulk_status == AS_UNLINK) {
|
|
|
|
as->bulk_status = 0; /* Only once */
|
2012-05-18 20:12:51 +08:00
|
|
|
urb = as->urb;
|
|
|
|
usb_get_urb(urb);
|
2009-09-01 23:09:56 +08:00
|
|
|
spin_unlock(&ps->lock); /* Allow completions */
|
2012-05-18 20:12:51 +08:00
|
|
|
usb_unlink_urb(urb);
|
|
|
|
usb_put_urb(urb);
|
2009-09-01 23:09:56 +08:00
|
|
|
spin_lock(&ps->lock);
|
|
|
|
goto rescan;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static void async_completed(struct urb *urb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-01-31 07:21:33 +08:00
|
|
|
struct async *as = urb->context;
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = as->ps;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct siginfo sinfo;
|
2009-07-09 01:09:23 +08:00
|
|
|
struct pid *pid = NULL;
|
|
|
|
u32 secid = 0;
|
2011-09-26 23:45:18 +08:00
|
|
|
const struct cred *cred = NULL;
|
2009-07-09 01:09:23 +08:00
|
|
|
int signr;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
spin_lock(&ps->lock);
|
|
|
|
list_move_tail(&as->asynclist, &ps->async_completed);
|
2007-08-25 03:42:52 +08:00
|
|
|
as->status = urb->status;
|
2009-07-09 01:09:23 +08:00
|
|
|
signr = as->signr;
|
|
|
|
if (signr) {
|
2005-04-17 06:20:36 +08:00
|
|
|
sinfo.si_signo = as->signr;
|
2007-08-25 03:42:52 +08:00
|
|
|
sinfo.si_errno = as->status;
|
2005-04-17 06:20:36 +08:00
|
|
|
sinfo.si_code = SI_ASYNCIO;
|
|
|
|
sinfo.si_addr = as->userurb;
|
2011-09-26 23:18:29 +08:00
|
|
|
pid = get_pid(as->pid);
|
2011-09-26 23:45:18 +08:00
|
|
|
cred = get_cred(as->cred);
|
2009-07-09 01:09:23 +08:00
|
|
|
secid = as->secid;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-06-21 12:15:16 +08:00
|
|
|
snoop(&urb->dev->dev, "urb complete\n");
|
2009-06-29 23:02:04 +08:00
|
|
|
snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
|
2012-07-04 15:18:03 +08:00
|
|
|
as->status, COMPLETE, NULL, 0);
|
|
|
|
if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN)
|
|
|
|
snoop_urb_data(urb, urb->actual_length);
|
|
|
|
|
2009-09-01 23:09:56 +08:00
|
|
|
if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
|
|
|
|
as->status != -ENOENT)
|
|
|
|
cancel_bulk_urbs(ps, as->bulk_addr);
|
2009-07-09 01:09:23 +08:00
|
|
|
spin_unlock(&ps->lock);
|
|
|
|
|
2011-09-26 23:18:29 +08:00
|
|
|
if (signr) {
|
2011-09-26 23:45:18 +08:00
|
|
|
kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid);
|
2011-09-26 23:18:29 +08:00
|
|
|
put_pid(pid);
|
2011-09-26 23:45:18 +08:00
|
|
|
put_cred(cred);
|
2011-09-26 23:18:29 +08:00
|
|
|
}
|
2009-07-09 01:09:23 +08:00
|
|
|
|
2005-06-21 12:15:16 +08:00
|
|
|
wake_up(&ps->wait);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static void destroy_async(struct usb_dev_state *ps, struct list_head *list)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-05-18 20:12:51 +08:00
|
|
|
struct urb *urb;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct async *as;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ps->lock, flags);
|
|
|
|
while (!list_empty(list)) {
|
|
|
|
as = list_entry(list->next, struct async, asynclist);
|
|
|
|
list_del_init(&as->asynclist);
|
2012-05-18 20:12:51 +08:00
|
|
|
urb = as->urb;
|
|
|
|
usb_get_urb(urb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* drop the spinlock so the completion handler can run */
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
2012-05-18 20:12:51 +08:00
|
|
|
usb_kill_urb(urb);
|
|
|
|
usb_put_urb(urb);
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_irqsave(&ps->lock, flags);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static void destroy_async_on_interface(struct usb_dev_state *ps,
|
2008-01-31 07:21:33 +08:00
|
|
|
unsigned int ifnum)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct list_head *p, *q, hitlist;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&hitlist);
|
|
|
|
spin_lock_irqsave(&ps->lock, flags);
|
|
|
|
list_for_each_safe(p, q, &ps->async_pending)
|
|
|
|
if (ifnum == list_entry(p, struct async, asynclist)->ifnum)
|
|
|
|
list_move_tail(p, &hitlist);
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
|
|
|
destroy_async(ps, &hitlist);
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static void destroy_all_async(struct usb_dev_state *ps)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-01-31 07:21:33 +08:00
|
|
|
destroy_async(ps, &ps->async_pending);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* interface claims are made only at the request of user level code,
|
|
|
|
* which can also release them (explicitly or by closing files).
|
|
|
|
* they're also undone when devices disconnect.
|
|
|
|
*/
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
static int driver_probe(struct usb_interface *intf,
|
|
|
|
const struct usb_device_id *id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void driver_disconnect(struct usb_interface *intf)
|
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = usb_get_intfdata(intf);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber;
|
|
|
|
|
|
|
|
if (!ps)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* NOTE: this relies on usbcore having canceled and completed
|
|
|
|
* all pending I/O requests; 2.6 does that.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (likely(ifnum < 8*sizeof(ps->ifclaimed)))
|
|
|
|
clear_bit(ifnum, &ps->ifclaimed);
|
|
|
|
else
|
2008-08-15 00:37:34 +08:00
|
|
|
dev_warn(&intf->dev, "interface number %u out of range\n",
|
|
|
|
ifnum);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
usb_set_intfdata(intf, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* force async requests to complete */
|
|
|
|
destroy_async_on_interface(ps, ifnum);
|
|
|
|
}
|
|
|
|
|
2007-12-05 03:35:15 +08:00
|
|
|
/* The following routines are merely placeholders. There is no way
|
|
|
|
* to inform a user task about suspend or resumes.
|
|
|
|
*/
|
|
|
|
static int driver_suspend(struct usb_interface *intf, pm_message_t msg)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int driver_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct usb_driver usbfs_driver = {
|
|
|
|
.name = "usbfs",
|
|
|
|
.probe = driver_probe,
|
|
|
|
.disconnect = driver_disconnect,
|
2007-12-05 03:35:15 +08:00
|
|
|
.suspend = driver_suspend,
|
|
|
|
.resume = driver_resume,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usb_device *dev = ps->dev;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (ifnum >= 8*sizeof(ps->ifclaimed))
|
|
|
|
return -EINVAL;
|
|
|
|
/* already claimed */
|
|
|
|
if (test_bit(ifnum, &ps->ifclaimed))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
intf = usb_ifnum_to_if(dev, ifnum);
|
|
|
|
if (!intf)
|
|
|
|
err = -ENOENT;
|
|
|
|
else
|
|
|
|
err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
|
|
|
|
if (err == 0)
|
|
|
|
set_bit(ifnum, &ps->ifclaimed);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int releaseintf(struct usb_dev_state *ps, unsigned int ifnum)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usb_device *dev;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
if (ifnum >= 8*sizeof(ps->ifclaimed))
|
|
|
|
return err;
|
|
|
|
dev = ps->dev;
|
|
|
|
intf = usb_ifnum_to_if(dev, ifnum);
|
|
|
|
if (!intf)
|
|
|
|
err = -ENOENT;
|
|
|
|
else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) {
|
|
|
|
usb_driver_release_interface(&usbfs_driver, intf);
|
|
|
|
err = 0;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int checkintf(struct usb_dev_state *ps, unsigned int ifnum)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (ps->dev->state != USB_STATE_CONFIGURED)
|
|
|
|
return -EHOSTUNREACH;
|
|
|
|
if (ifnum >= 8*sizeof(ps->ifclaimed))
|
|
|
|
return -EINVAL;
|
|
|
|
if (test_bit(ifnum, &ps->ifclaimed))
|
|
|
|
return 0;
|
|
|
|
/* if not yet claimed, claim it for the driver */
|
2008-01-31 07:21:33 +08:00
|
|
|
dev_warn(&ps->dev->dev, "usbfs: process %d (%s) did not claim "
|
|
|
|
"interface %u before use\n", task_pid_nr(current),
|
|
|
|
current->comm, ifnum);
|
2005-04-17 06:20:36 +08:00
|
|
|
return claimintf(ps, ifnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int findintfep(struct usb_device *dev, unsigned int ep)
|
|
|
|
{
|
|
|
|
unsigned int i, j, e;
|
2008-01-31 07:21:33 +08:00
|
|
|
struct usb_interface *intf;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct usb_host_interface *alts;
|
|
|
|
struct usb_endpoint_descriptor *endpt;
|
|
|
|
|
|
|
|
if (ep & ~(USB_DIR_IN|0xf))
|
|
|
|
return -EINVAL;
|
|
|
|
if (!dev->actconfig)
|
|
|
|
return -ESRCH;
|
|
|
|
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
|
|
|
|
intf = dev->actconfig->interface[i];
|
|
|
|
for (j = 0; j < intf->num_altsetting; j++) {
|
2008-01-31 07:21:33 +08:00
|
|
|
alts = &intf->altsetting[j];
|
2005-04-17 06:20:36 +08:00
|
|
|
for (e = 0; e < alts->desc.bNumEndpoints; e++) {
|
|
|
|
endpt = &alts->endpoint[e].desc;
|
|
|
|
if (endpt->bEndpointAddress == ep)
|
|
|
|
return alts->desc.bInterfaceNumber;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-01-31 07:21:33 +08:00
|
|
|
return -ENOENT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int check_ctrlrecip(struct usb_dev_state *ps, unsigned int requesttype,
|
2011-09-25 20:26:25 +08:00
|
|
|
unsigned int request, unsigned int index)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2011-09-25 20:26:25 +08:00
|
|
|
struct usb_host_interface *alt_setting;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-02-18 22:43:47 +08:00
|
|
|
if (ps->dev->state != USB_STATE_UNAUTHENTICATED
|
|
|
|
&& ps->dev->state != USB_STATE_ADDRESS
|
2006-03-11 16:16:55 +08:00
|
|
|
&& ps->dev->state != USB_STATE_CONFIGURED)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EHOSTUNREACH;
|
|
|
|
if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
|
|
|
|
return 0;
|
|
|
|
|
2011-09-25 20:26:25 +08:00
|
|
|
/*
|
|
|
|
* check for the special corner case 'get_device_id' in the printer
|
2013-07-12 16:04:18 +08:00
|
|
|
* class specification, which we always want to allow as it is used
|
|
|
|
* to query things like ink level, etc.
|
2011-09-25 20:26:25 +08:00
|
|
|
*/
|
|
|
|
if (requesttype == 0xa1 && request == 0) {
|
|
|
|
alt_setting = usb_find_alt_setting(ps->dev->actconfig,
|
|
|
|
index >> 8, index & 0xff);
|
|
|
|
if (alt_setting
|
|
|
|
&& alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER)
|
2013-07-12 16:04:18 +08:00
|
|
|
return 0;
|
2011-09-25 20:26:25 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
index &= 0xff;
|
|
|
|
switch (requesttype & USB_RECIP_MASK) {
|
|
|
|
case USB_RECIP_ENDPOINT:
|
2013-04-16 17:08:33 +08:00
|
|
|
if ((index & ~USB_DIR_IN) == 0)
|
|
|
|
return 0;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = findintfep(ps->dev, index);
|
2013-09-24 20:13:48 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
/*
|
|
|
|
* Some not fully compliant Win apps seem to get
|
|
|
|
* index wrong and have the endpoint number here
|
|
|
|
* rather than the endpoint address (with the
|
|
|
|
* correct direction). Win does let this through,
|
|
|
|
* so we'll not reject it here but leave it to
|
|
|
|
* the device to not break KVM. But we warn.
|
|
|
|
*/
|
|
|
|
ret = findintfep(ps->dev, index ^ 0x80);
|
|
|
|
if (ret >= 0)
|
|
|
|
dev_info(&ps->dev->dev,
|
|
|
|
"%s: process %i (%s) requesting ep %02x but needs %02x\n",
|
|
|
|
__func__, task_pid_nr(current),
|
|
|
|
current->comm, index, index ^ 0x80);
|
|
|
|
}
|
2008-01-31 07:21:33 +08:00
|
|
|
if (ret >= 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = checkintf(ps, ret);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_RECIP_INTERFACE:
|
|
|
|
ret = checkintf(ps, index);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-09 23:19:30 +08:00
|
|
|
static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev,
|
|
|
|
unsigned char ep)
|
|
|
|
{
|
|
|
|
if (ep & USB_ENDPOINT_DIR_MASK)
|
|
|
|
return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK];
|
|
|
|
else
|
|
|
|
return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK];
|
|
|
|
}
|
|
|
|
|
2014-04-02 08:06:09 +08:00
|
|
|
static int parse_usbdevfs_streams(struct usb_dev_state *ps,
|
2013-10-09 23:19:31 +08:00
|
|
|
struct usbdevfs_streams __user *streams,
|
|
|
|
unsigned int *num_streams_ret,
|
|
|
|
unsigned int *num_eps_ret,
|
|
|
|
struct usb_host_endpoint ***eps_ret,
|
|
|
|
struct usb_interface **intf_ret)
|
|
|
|
{
|
|
|
|
unsigned int i, num_streams, num_eps;
|
|
|
|
struct usb_host_endpoint **eps;
|
|
|
|
struct usb_interface *intf = NULL;
|
|
|
|
unsigned char ep;
|
|
|
|
int ifnum, ret;
|
|
|
|
|
|
|
|
if (get_user(num_streams, &streams->num_streams) ||
|
|
|
|
get_user(num_eps, &streams->num_eps))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (num_eps < 1 || num_eps > USB_MAXENDPOINTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* The XHCI controller allows max 2 ^ 16 streams */
|
|
|
|
if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL);
|
|
|
|
if (!eps)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < num_eps; i++) {
|
|
|
|
if (get_user(ep, &streams->eps[i])) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
eps[i] = ep_to_host_endpoint(ps->dev, ep);
|
|
|
|
if (!eps[i]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* usb_alloc/free_streams operate on an usb_interface */
|
|
|
|
ifnum = findintfep(ps->dev, ep);
|
|
|
|
if (ifnum < 0) {
|
|
|
|
ret = ifnum;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
ret = checkintf(ps, ifnum);
|
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
intf = usb_ifnum_to_if(ps->dev, ifnum);
|
|
|
|
} else {
|
|
|
|
/* Verify all eps belong to the same interface */
|
|
|
|
if (ifnum != intf->altsetting->desc.bInterfaceNumber) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_streams_ret)
|
|
|
|
*num_streams_ret = num_streams;
|
|
|
|
*num_eps_ret = num_eps;
|
|
|
|
*eps_ret = eps;
|
|
|
|
*intf_ret = intf;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
kfree(eps);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-06-25 02:47:12 +08:00
|
|
|
static int match_devt(struct device *dev, void *data)
|
2005-06-21 12:15:16 +08:00
|
|
|
{
|
2008-07-02 19:28:55 +08:00
|
|
|
return dev->devt == (dev_t) (unsigned long) data;
|
2007-03-13 22:59:31 +08:00
|
|
|
}
|
2005-06-21 12:15:16 +08:00
|
|
|
|
2008-06-25 02:47:12 +08:00
|
|
|
static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
|
2007-03-13 22:59:31 +08:00
|
|
|
{
|
|
|
|
struct device *dev;
|
|
|
|
|
2008-07-02 19:28:55 +08:00
|
|
|
dev = bus_find_device(&usb_bus_type, NULL,
|
|
|
|
(void *) (unsigned long) devt, match_devt);
|
2007-03-13 22:59:31 +08:00
|
|
|
if (!dev)
|
|
|
|
return NULL;
|
|
|
|
return container_of(dev, struct usb_device, dev);
|
|
|
|
}
|
2005-06-21 12:15:16 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* file operations
|
|
|
|
*/
|
|
|
|
static int usbdev_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
2005-07-31 07:05:53 +08:00
|
|
|
struct usb_device *dev = NULL;
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps;
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
2014-03-10 16:36:40 +08:00
|
|
|
ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
|
2008-01-31 07:21:33 +08:00
|
|
|
if (!ps)
|
2010-01-09 01:56:19 +08:00
|
|
|
goto out_free_ps;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-07-31 03:28:14 +08:00
|
|
|
ret = -ENODEV;
|
2008-06-25 02:47:12 +08:00
|
|
|
|
2010-01-09 01:56:19 +08:00
|
|
|
/* Protect against simultaneous removal or release */
|
|
|
|
mutex_lock(&usbfs_mutex);
|
|
|
|
|
2007-03-13 22:59:31 +08:00
|
|
|
/* usbdev device-node */
|
2005-07-31 07:05:53 +08:00
|
|
|
if (imajor(inode) == USB_DEVICE_MAJOR)
|
2008-06-25 02:47:12 +08:00
|
|
|
dev = usbdev_lookup_by_devt(inode->i_rdev);
|
2010-01-09 01:56:19 +08:00
|
|
|
|
|
|
|
mutex_unlock(&usbfs_mutex);
|
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
goto out_free_ps;
|
|
|
|
|
|
|
|
usb_lock_device(dev);
|
|
|
|
if (dev->state == USB_STATE_NOTATTACHED)
|
|
|
|
goto out_unlock_device;
|
|
|
|
|
2006-11-21 00:38:46 +08:00
|
|
|
ret = usb_autoresume_device(dev);
|
2006-08-31 03:47:18 +08:00
|
|
|
if (ret)
|
2010-01-09 01:56:19 +08:00
|
|
|
goto out_unlock_device;
|
2006-08-31 03:47:18 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
ps->dev = dev;
|
|
|
|
ps->file = file;
|
|
|
|
spin_lock_init(&ps->lock);
|
2006-12-13 16:03:38 +08:00
|
|
|
INIT_LIST_HEAD(&ps->list);
|
2005-04-17 06:20:36 +08:00
|
|
|
INIT_LIST_HEAD(&ps->async_pending);
|
|
|
|
INIT_LIST_HEAD(&ps->async_completed);
|
|
|
|
init_waitqueue_head(&ps->wait);
|
|
|
|
ps->discsignr = 0;
|
2006-10-02 17:17:28 +08:00
|
|
|
ps->disc_pid = get_pid(task_pid(current));
|
2011-09-26 23:45:18 +08:00
|
|
|
ps->cred = get_current_cred();
|
2005-04-17 06:20:36 +08:00
|
|
|
ps->disccontext = NULL;
|
|
|
|
ps->ifclaimed = 0;
|
2006-06-30 16:55:48 +08:00
|
|
|
security_task_getsecid(current, &ps->secid);
|
2007-04-21 02:50:48 +08:00
|
|
|
smp_wmb();
|
2005-04-17 06:20:36 +08:00
|
|
|
list_add_tail(&ps->list, &dev->filelist);
|
|
|
|
file->private_data = ps;
|
2010-01-09 01:56:19 +08:00
|
|
|
usb_unlock_device(dev);
|
2008-10-06 23:24:26 +08:00
|
|
|
snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current),
|
|
|
|
current->comm);
|
2010-01-09 01:56:19 +08:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
out_unlock_device:
|
|
|
|
usb_unlock_device(dev);
|
|
|
|
usb_put_dev(dev);
|
|
|
|
out_free_ps:
|
|
|
|
kfree(ps);
|
usbfs: private mutex for open, release, and remove
The usbfs code doesn't provide sufficient mutual exclusion among open,
release, and remove. Release vs. remove is okay because they both
acquire the device lock, but open is not exclusive with either one. All
three routines modify the udev->filelist linked list, so they must not
run concurrently.
Apparently someone gave this a minimum amount of thought in the past by
explicitly acquiring the BKL at the start of the usbdev_open routine.
Oddly enough, there's a comment pointing out that locking is unnecessary
because chrdev_open already has acquired the BKL.
But this ignores the point that the files in /proc/bus/usb/* are not
char device files; they are regular files and so they don't get any
special locking. Furthermore it's necessary to acquire the same lock in
the release and remove routines, which the code does not do.
Yet another problem arises because the same file_operations structure is
accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file
nodes. Even when one of them has been removed, it's still possible for
userspace to open the other. So simple locking around the individual
remove routines is insufficient; we need to lock the entire
usb_notify_remove_device notifier chain.
Rather than rely on the BKL, this patch (as723) introduces a new private
mutex for the purpose. Holding the BKL while invoking a notifier chain
doesn't seem like a good idea.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-07-02 10:05:01 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int usbdev_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = file->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct usb_device *dev = ps->dev;
|
|
|
|
unsigned int ifnum;
|
2009-03-10 01:44:02 +08:00
|
|
|
struct async *as;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
usb_lock_device(dev);
|
2009-06-29 22:56:54 +08:00
|
|
|
usb_hub_release_all_ports(dev, ps);
|
usbfs: private mutex for open, release, and remove
The usbfs code doesn't provide sufficient mutual exclusion among open,
release, and remove. Release vs. remove is okay because they both
acquire the device lock, but open is not exclusive with either one. All
three routines modify the udev->filelist linked list, so they must not
run concurrently.
Apparently someone gave this a minimum amount of thought in the past by
explicitly acquiring the BKL at the start of the usbdev_open routine.
Oddly enough, there's a comment pointing out that locking is unnecessary
because chrdev_open already has acquired the BKL.
But this ignores the point that the files in /proc/bus/usb/* are not
char device files; they are regular files and so they don't get any
special locking. Furthermore it's necessary to acquire the same lock in
the release and remove routines, which the code does not do.
Yet another problem arises because the same file_operations structure is
accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file
nodes. Even when one of them has been removed, it's still possible for
userspace to open the other. So simple locking around the individual
remove routines is insufficient; we need to lock the entire
usb_notify_remove_device notifier chain.
Rather than rely on the BKL, this patch (as723) introduces a new private
mutex for the purpose. Holding the BKL while invoking a notifier chain
doesn't seem like a good idea.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-07-02 10:05:01 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
list_del_init(&ps->list);
|
usbfs: private mutex for open, release, and remove
The usbfs code doesn't provide sufficient mutual exclusion among open,
release, and remove. Release vs. remove is okay because they both
acquire the device lock, but open is not exclusive with either one. All
three routines modify the udev->filelist linked list, so they must not
run concurrently.
Apparently someone gave this a minimum amount of thought in the past by
explicitly acquiring the BKL at the start of the usbdev_open routine.
Oddly enough, there's a comment pointing out that locking is unnecessary
because chrdev_open already has acquired the BKL.
But this ignores the point that the files in /proc/bus/usb/* are not
char device files; they are regular files and so they don't get any
special locking. Furthermore it's necessary to acquire the same lock in
the release and remove routines, which the code does not do.
Yet another problem arises because the same file_operations structure is
accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file
nodes. Even when one of them has been removed, it's still possible for
userspace to open the other. So simple locking around the individual
remove routines is insufficient; we need to lock the entire
usb_notify_remove_device notifier chain.
Rather than rely on the BKL, this patch (as723) introduces a new private
mutex for the purpose. Holding the BKL while invoking a notifier chain
doesn't seem like a good idea.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-07-02 10:05:01 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
|
|
|
|
ifnum++) {
|
|
|
|
if (test_bit(ifnum, &ps->ifclaimed))
|
|
|
|
releaseintf(ps, ifnum);
|
|
|
|
}
|
|
|
|
destroy_all_async(ps);
|
2006-11-21 00:38:46 +08:00
|
|
|
usb_autosuspend_device(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_unlock_device(dev);
|
|
|
|
usb_put_dev(dev);
|
2006-10-02 17:17:28 +08:00
|
|
|
put_pid(ps->disc_pid);
|
2011-09-26 23:45:18 +08:00
|
|
|
put_cred(ps->cred);
|
2009-03-10 01:44:02 +08:00
|
|
|
|
|
|
|
as = async_getcompleted(ps);
|
|
|
|
while (as) {
|
|
|
|
free_async(as);
|
|
|
|
as = async_getcompleted(ps);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(ps);
|
usbfs: private mutex for open, release, and remove
The usbfs code doesn't provide sufficient mutual exclusion among open,
release, and remove. Release vs. remove is okay because they both
acquire the device lock, but open is not exclusive with either one. All
three routines modify the udev->filelist linked list, so they must not
run concurrently.
Apparently someone gave this a minimum amount of thought in the past by
explicitly acquiring the BKL at the start of the usbdev_open routine.
Oddly enough, there's a comment pointing out that locking is unnecessary
because chrdev_open already has acquired the BKL.
But this ignores the point that the files in /proc/bus/usb/* are not
char device files; they are regular files and so they don't get any
special locking. Furthermore it's necessary to acquire the same lock in
the release and remove routines, which the code does not do.
Yet another problem arises because the same file_operations structure is
accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file
nodes. Even when one of them has been removed, it's still possible for
userspace to open the other. So simple locking around the individual
remove routines is insufficient; we need to lock the entire
usb_notify_remove_device notifier chain.
Rather than rely on the BKL, this patch (as723) introduces a new private
mutex for the purpose. Holding the BKL while invoking a notifier chain
doesn't seem like a good idea.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-07-02 10:05:01 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_control(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usb_device *dev = ps->dev;
|
|
|
|
struct usbdevfs_ctrltransfer ctrl;
|
|
|
|
unsigned int tmo;
|
|
|
|
unsigned char *tbuf;
|
2008-03-13 04:32:24 +08:00
|
|
|
unsigned wLength;
|
2009-06-29 23:02:04 +08:00
|
|
|
int i, pipe, ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
|
|
|
|
return -EFAULT;
|
2011-09-25 20:26:25 +08:00
|
|
|
ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest,
|
|
|
|
ctrl.wIndex);
|
2008-01-31 07:21:33 +08:00
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-03-13 04:32:24 +08:00
|
|
|
wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */
|
|
|
|
if (wLength > PAGE_SIZE)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2011-11-18 05:41:25 +08:00
|
|
|
ret = usbfs_increase_memory_usage(PAGE_SIZE + sizeof(struct urb) +
|
|
|
|
sizeof(struct usb_ctrlrequest));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2008-01-31 07:21:33 +08:00
|
|
|
tbuf = (unsigned char *)__get_free_page(GFP_KERNEL);
|
2011-11-18 05:41:25 +08:00
|
|
|
if (!tbuf) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto done;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
tmo = ctrl.timeout;
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop(&dev->dev, "control urb: bRequestType=%02x "
|
|
|
|
"bRequest=%02x wValue=%04x "
|
|
|
|
"wIndex=%04x wLength=%04x\n",
|
2013-08-31 23:09:14 +08:00
|
|
|
ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
|
|
|
|
ctrl.wIndex, ctrl.wLength);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ctrl.bRequestType & 0x80) {
|
2008-01-31 07:21:33 +08:00
|
|
|
if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
|
|
|
|
ctrl.wLength)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto done;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2009-06-29 23:02:04 +08:00
|
|
|
pipe = usb_rcvctrlpipe(dev, 0);
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, NULL, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
usb_unlock_device(dev);
|
2009-06-29 23:02:04 +08:00
|
|
|
i = usb_control_msg(dev, pipe, ctrl.bRequest,
|
2008-01-31 07:21:33 +08:00
|
|
|
ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
|
|
|
|
tbuf, ctrl.wLength, tmo);
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_lock_device(dev);
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE,
|
2011-03-15 23:41:47 +08:00
|
|
|
tbuf, max(i, 0));
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((i > 0) && ctrl.wLength) {
|
2005-07-30 03:16:58 +08:00
|
|
|
if (copy_to_user(ctrl.data, tbuf, i)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto done;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ctrl.wLength) {
|
|
|
|
if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto done;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2009-06-29 23:02:04 +08:00
|
|
|
pipe = usb_sndctrlpipe(dev, 0);
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT,
|
|
|
|
tbuf, ctrl.wLength);
|
2009-06-29 23:02:04 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_unlock_device(dev);
|
2008-01-31 07:21:33 +08:00
|
|
|
i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest,
|
|
|
|
ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
|
|
|
|
tbuf, ctrl.wLength, tmo);
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_lock_device(dev);
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, NULL, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-01-31 07:21:33 +08:00
|
|
|
if (i < 0 && i != -EPIPE) {
|
2005-04-17 06:20:36 +08:00
|
|
|
dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL "
|
|
|
|
"failed cmd %s rqt %u rq %u len %u ret %d\n",
|
|
|
|
current->comm, ctrl.bRequestType, ctrl.bRequest,
|
|
|
|
ctrl.wLength, i);
|
|
|
|
}
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = i;
|
|
|
|
done:
|
|
|
|
free_page((unsigned long) tbuf);
|
2011-11-18 05:41:25 +08:00
|
|
|
usbfs_decrease_memory_usage(PAGE_SIZE + sizeof(struct urb) +
|
|
|
|
sizeof(struct usb_ctrlrequest));
|
2011-11-18 05:41:14 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usb_device *dev = ps->dev;
|
|
|
|
struct usbdevfs_bulktransfer bulk;
|
|
|
|
unsigned int tmo, len1, pipe;
|
|
|
|
int len2;
|
|
|
|
unsigned char *tbuf;
|
2009-06-29 23:02:04 +08:00
|
|
|
int i, ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&bulk, arg, sizeof(bulk)))
|
|
|
|
return -EFAULT;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = findintfep(ps->dev, bulk.ep);
|
|
|
|
if (ret < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = checkintf(ps, ret);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
if (bulk.ep & USB_DIR_IN)
|
|
|
|
pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f);
|
|
|
|
else
|
|
|
|
pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f);
|
|
|
|
if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
|
|
|
|
return -EINVAL;
|
|
|
|
len1 = bulk.len;
|
2011-11-18 05:41:35 +08:00
|
|
|
if (len1 >= USBFS_XFER_MAX)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2011-11-18 05:41:25 +08:00
|
|
|
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (!(tbuf = kmalloc(len1, GFP_KERNEL))) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto done;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
tmo = bulk.timeout;
|
|
|
|
if (bulk.ep & 0x80) {
|
|
|
|
if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto done;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
|
2009-06-29 23:02:04 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_unlock_device(dev);
|
|
|
|
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
|
|
|
|
usb_lock_device(dev);
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2);
|
2009-06-29 23:02:04 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!i && len2) {
|
|
|
|
if (copy_to_user(bulk.data, tbuf, len2)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto done;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (len1) {
|
|
|
|
if (copy_from_user(tbuf, bulk.data, len1)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto done;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1);
|
2009-06-29 23:02:04 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_unlock_device(dev);
|
|
|
|
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
|
|
|
|
usb_lock_device(dev);
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = (i < 0 ? i : len2);
|
|
|
|
done:
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(tbuf);
|
2011-11-18 05:41:25 +08:00
|
|
|
usbfs_decrease_memory_usage(len1 + sizeof(struct urb));
|
2011-11-18 05:41:14 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-02-20 23:49:30 +08:00
|
|
|
static void check_reset_of_active_ep(struct usb_device *udev,
|
|
|
|
unsigned int epnum, char *ioctl_name)
|
|
|
|
{
|
|
|
|
struct usb_host_endpoint **eps;
|
|
|
|
struct usb_host_endpoint *ep;
|
|
|
|
|
|
|
|
eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out;
|
|
|
|
ep = eps[epnum & 0x0f];
|
|
|
|
if (ep && !list_empty(&ep->urb_list))
|
|
|
|
dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n",
|
|
|
|
task_pid_nr(current), current->comm,
|
|
|
|
ioctl_name, epnum);
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_resetep(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned int ep;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (get_user(ep, (unsigned int __user *)arg))
|
|
|
|
return -EFAULT;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = findintfep(ps->dev, ep);
|
|
|
|
if (ret < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = checkintf(ps, ret);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2014-02-20 23:49:30 +08:00
|
|
|
check_reset_of_active_ep(ps->dev, ep, "RESETEP");
|
2009-04-09 01:36:28 +08:00
|
|
|
usb_reset_endpoint(ps->dev, ep);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_clearhalt(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned int ep;
|
|
|
|
int pipe;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (get_user(ep, (unsigned int __user *)arg))
|
|
|
|
return -EFAULT;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = findintfep(ps->dev, ep);
|
|
|
|
if (ret < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = checkintf(ps, ret);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2014-02-20 23:49:30 +08:00
|
|
|
check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT");
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ep & USB_DIR_IN)
|
2008-01-31 07:21:33 +08:00
|
|
|
pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
|
|
|
|
else
|
|
|
|
pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return usb_clear_halt(ps->dev, pipe);
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_getdriver(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_getdriver gd;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (copy_from_user(&gd, arg, sizeof(gd)))
|
|
|
|
return -EFAULT;
|
|
|
|
intf = usb_ifnum_to_if(ps->dev, gd.interface);
|
|
|
|
if (!intf || !intf->dev.driver)
|
|
|
|
ret = -ENODATA;
|
|
|
|
else {
|
2013-02-02 15:57:53 +08:00
|
|
|
strlcpy(gd.driver, intf->dev.driver->name,
|
2005-04-17 06:20:36 +08:00
|
|
|
sizeof(gd.driver));
|
|
|
|
ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-11-06 22:41:28 +08:00
|
|
|
struct usbdevfs_connectinfo ci = {
|
|
|
|
.devnum = ps->dev->devnum,
|
|
|
|
.slow = ps->dev->speed == USB_SPEED_LOW
|
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (copy_to_user(arg, &ci, sizeof(ci)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_resetdevice(struct usb_dev_state *ps)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-18 22:00:29 +08:00
|
|
|
return usb_reset_device(ps->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_setintf(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_setinterface setintf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (copy_from_user(&setintf, arg, sizeof(setintf)))
|
|
|
|
return -EFAULT;
|
|
|
|
if ((ret = checkintf(ps, setintf.interface)))
|
|
|
|
return ret;
|
2013-10-09 23:19:27 +08:00
|
|
|
|
|
|
|
destroy_async_on_interface(ps, setintf.interface);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return usb_set_interface(ps->dev, setintf.interface,
|
|
|
|
setintf.altsetting);
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_setconfig(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-02-09 05:40:43 +08:00
|
|
|
int u;
|
2005-04-17 06:20:36 +08:00
|
|
|
int status = 0;
|
2008-01-31 07:21:33 +08:00
|
|
|
struct usb_host_config *actconfig;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-02-09 05:40:43 +08:00
|
|
|
if (get_user(u, (int __user *)arg))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
actconfig = ps->dev->actconfig;
|
|
|
|
|
|
|
|
/* Don't touch the device if any interfaces are claimed.
|
|
|
|
* It could interfere with other drivers' operations, and if
|
2005-04-17 06:20:36 +08:00
|
|
|
* an interface is claimed by usbfs it could easily deadlock.
|
|
|
|
*/
|
2008-01-31 07:21:33 +08:00
|
|
|
if (actconfig) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
|
|
|
|
if (usb_interface_claimed(actconfig->interface[i])) {
|
|
|
|
dev_warn(&ps->dev->dev,
|
2005-04-12 09:34:17 +08:00
|
|
|
"usbfs: interface %d claimed by %s "
|
2005-04-17 06:20:36 +08:00
|
|
|
"while '%s' sets config #%d\n",
|
|
|
|
actconfig->interface[i]
|
|
|
|
->cur_altsetting
|
|
|
|
->desc.bInterfaceNumber,
|
2005-04-12 09:34:17 +08:00
|
|
|
actconfig->interface[i]
|
|
|
|
->dev.driver->name,
|
2005-04-17 06:20:36 +08:00
|
|
|
current->comm, u);
|
2008-01-31 07:21:33 +08:00
|
|
|
status = -EBUSY;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
2008-01-31 07:21:33 +08:00
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* SET_CONFIGURATION is often abused as a "cheap" driver reset,
|
|
|
|
* so avoid usb_set_configuration()'s kick to sysfs
|
|
|
|
*/
|
|
|
|
if (status == 0) {
|
|
|
|
if (actconfig && actconfig->desc.bConfigurationValue == u)
|
|
|
|
status = usb_reset_configuration(ps->dev);
|
|
|
|
else
|
|
|
|
status = usb_set_configuration(ps->dev, u);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,
|
2008-01-31 07:21:33 +08:00
|
|
|
struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
|
|
|
|
void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_iso_packet_desc *isopkt = NULL;
|
|
|
|
struct usb_host_endpoint *ep;
|
2011-11-18 05:41:14 +08:00
|
|
|
struct async *as = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct usb_ctrlrequest *dr = NULL;
|
|
|
|
unsigned int u, totlen, isofrmlen;
|
2012-07-04 15:18:03 +08:00
|
|
|
int i, ret, is_in, num_sgs = 0, ifnum = -1;
|
2013-10-09 23:19:28 +08:00
|
|
|
int number_of_packets = 0;
|
2013-10-09 23:19:29 +08:00
|
|
|
unsigned int stream_id = 0;
|
2012-07-04 15:18:03 +08:00
|
|
|
void *buf;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-17 22:18:11 +08:00
|
|
|
if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
|
|
|
|
USBDEVFS_URB_SHORT_NOT_OK |
|
2009-09-01 23:09:56 +08:00
|
|
|
USBDEVFS_URB_BULK_CONTINUATION |
|
2008-04-17 22:18:11 +08:00
|
|
|
USBDEVFS_URB_NO_FSBR |
|
|
|
|
USBDEVFS_URB_ZERO_PACKET |
|
|
|
|
USBDEVFS_URB_NO_INTERRUPT))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2009-06-29 23:04:54 +08:00
|
|
|
if (uurb->buffer_length > 0 && !uurb->buffer)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2008-01-31 07:21:33 +08:00
|
|
|
if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
|
|
|
|
(uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
|
|
|
|
ifnum = findintfep(ps->dev, uurb->endpoint);
|
|
|
|
if (ifnum < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ifnum;
|
2008-01-31 07:21:33 +08:00
|
|
|
ret = checkintf(ps, ifnum);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2013-10-09 23:19:30 +08:00
|
|
|
ep = ep_to_host_endpoint(ps->dev, uurb->endpoint);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!ep)
|
|
|
|
return -ENOENT;
|
2013-10-09 23:19:30 +08:00
|
|
|
is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0;
|
2011-11-18 05:41:25 +08:00
|
|
|
|
|
|
|
u = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
switch(uurb->type) {
|
|
|
|
case USBDEVFS_URB_TYPE_CONTROL:
|
2007-07-31 05:09:28 +08:00
|
|
|
if (!usb_endpoint_xfer_control(&ep->desc))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2011-11-18 05:41:25 +08:00
|
|
|
/* min 8 byte setup packet */
|
|
|
|
if (uurb->buffer_length < 8)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2008-01-31 07:21:33 +08:00
|
|
|
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
|
|
|
if (!dr)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(dr, uurb->buffer, 8)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-09-25 20:26:25 +08:00
|
|
|
ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
|
2008-01-31 07:21:33 +08:00
|
|
|
le16_to_cpup(&dr->wIndex));
|
2011-11-18 05:41:14 +08:00
|
|
|
if (ret)
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
uurb->buffer_length = le16_to_cpup(&dr->wLength);
|
|
|
|
uurb->buffer += 8;
|
2007-07-31 05:09:28 +08:00
|
|
|
if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
|
|
|
|
is_in = 1;
|
|
|
|
uurb->endpoint |= USB_DIR_IN;
|
|
|
|
} else {
|
|
|
|
is_in = 0;
|
|
|
|
uurb->endpoint &= ~USB_DIR_IN;
|
|
|
|
}
|
2010-01-27 06:07:29 +08:00
|
|
|
snoop(&ps->dev->dev, "control urb: bRequestType=%02x "
|
|
|
|
"bRequest=%02x wValue=%04x "
|
|
|
|
"wIndex=%04x wLength=%04x\n",
|
|
|
|
dr->bRequestType, dr->bRequest,
|
|
|
|
__le16_to_cpup(&dr->wValue),
|
|
|
|
__le16_to_cpup(&dr->wIndex),
|
|
|
|
__le16_to_cpup(&dr->wLength));
|
2011-11-18 05:41:25 +08:00
|
|
|
u = sizeof(struct usb_ctrlrequest);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_URB_TYPE_BULK:
|
2007-07-31 05:09:28 +08:00
|
|
|
switch (usb_endpoint_type(&ep->desc)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
return -EINVAL;
|
2009-12-12 05:20:20 +08:00
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
/* allow single-shot interrupt transfers */
|
|
|
|
uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
|
|
|
|
goto interrupt_urb;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-07-04 15:18:03 +08:00
|
|
|
num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE);
|
|
|
|
if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
|
|
|
|
num_sgs = 0;
|
2013-10-09 23:19:29 +08:00
|
|
|
if (ep->streams)
|
|
|
|
stream_id = uurb->stream_id;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
2009-12-12 05:20:20 +08:00
|
|
|
case USBDEVFS_URB_TYPE_INTERRUPT:
|
|
|
|
if (!usb_endpoint_xfer_int(&ep->desc))
|
|
|
|
return -EINVAL;
|
|
|
|
interrupt_urb:
|
|
|
|
break;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
case USBDEVFS_URB_TYPE_ISO:
|
|
|
|
/* arbitrary limit */
|
2008-01-31 07:21:33 +08:00
|
|
|
if (uurb->number_of_packets < 1 ||
|
|
|
|
uurb->number_of_packets > 128)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2007-07-31 05:09:28 +08:00
|
|
|
if (!usb_endpoint_xfer_isoc(&ep->desc))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2013-10-09 23:19:28 +08:00
|
|
|
number_of_packets = uurb->number_of_packets;
|
2008-01-31 07:21:33 +08:00
|
|
|
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
|
2013-10-09 23:19:28 +08:00
|
|
|
number_of_packets;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
|
|
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-10-09 23:19:28 +08:00
|
|
|
for (totlen = u = 0; u < number_of_packets; u++) {
|
2013-05-25 00:18:48 +08:00
|
|
|
/*
|
|
|
|
* arbitrary limit need for USB 3.0
|
|
|
|
* bMaxBurst (0~15 allowed, 1~16 packets)
|
|
|
|
* bmAttributes (bit 1:0, mult 0~2, 1~3 packets)
|
|
|
|
* sizemax: 1024 * 16 * 3 = 49152
|
|
|
|
*/
|
|
|
|
if (isopkt[u].length > 49152) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
totlen += isopkt[u].length;
|
|
|
|
}
|
2011-11-18 05:41:25 +08:00
|
|
|
u *= sizeof(struct usb_iso_packet_descriptor);
|
2005-04-17 06:20:36 +08:00
|
|
|
uurb->buffer_length = totlen;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-11-18 05:41:25 +08:00
|
|
|
|
2011-11-18 05:41:35 +08:00
|
|
|
if (uurb->buffer_length >= USBFS_XFER_MAX) {
|
2011-11-18 05:41:25 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
2009-06-29 23:04:54 +08:00
|
|
|
if (uurb->buffer_length > 0 &&
|
|
|
|
!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
|
|
|
|
uurb->buffer, uurb->buffer_length)) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-10-09 23:19:28 +08:00
|
|
|
as = alloc_async(number_of_packets);
|
2009-06-29 23:04:54 +08:00
|
|
|
if (!as) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-07-04 15:18:03 +08:00
|
|
|
|
|
|
|
u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
|
|
|
|
num_sgs * sizeof(struct scatterlist);
|
2011-11-18 05:41:25 +08:00
|
|
|
ret = usbfs_increase_memory_usage(u);
|
|
|
|
if (ret)
|
|
|
|
goto error;
|
|
|
|
as->mem_usage = u;
|
|
|
|
|
2012-07-04 15:18:03 +08:00
|
|
|
if (num_sgs) {
|
|
|
|
as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!as->urb->sg) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
as->urb->num_sgs = num_sgs;
|
|
|
|
sg_init_table(as->urb->sg, as->urb->num_sgs);
|
|
|
|
|
|
|
|
totlen = uurb->buffer_length;
|
|
|
|
for (i = 0; i < as->urb->num_sgs; i++) {
|
|
|
|
u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen;
|
|
|
|
buf = kmalloc(u, GFP_KERNEL);
|
|
|
|
if (!buf) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
sg_set_buf(&as->urb->sg[i], buf, u);
|
|
|
|
|
|
|
|
if (!is_in) {
|
|
|
|
if (copy_from_user(buf, uurb->buffer, u)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto error;
|
|
|
|
}
|
2012-10-13 18:20:36 +08:00
|
|
|
uurb->buffer += u;
|
2012-07-04 15:18:03 +08:00
|
|
|
}
|
|
|
|
totlen -= u;
|
|
|
|
}
|
|
|
|
} else if (uurb->buffer_length > 0) {
|
2009-06-29 23:04:54 +08:00
|
|
|
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!as->urb->transfer_buffer) {
|
2011-11-18 05:41:14 +08:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
2009-06-29 23:04:54 +08:00
|
|
|
}
|
2012-07-04 15:18:03 +08:00
|
|
|
|
|
|
|
if (!is_in) {
|
|
|
|
if (copy_from_user(as->urb->transfer_buffer,
|
|
|
|
uurb->buffer,
|
|
|
|
uurb->buffer_length)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
|
|
|
|
/*
|
|
|
|
* Isochronous input data may end up being
|
|
|
|
* discontiguous if some of the packets are short.
|
|
|
|
* Clear the buffer so that the gaps don't leak
|
|
|
|
* kernel data to userspace.
|
|
|
|
*/
|
2010-03-07 04:04:03 +08:00
|
|
|
memset(as->urb->transfer_buffer, 0,
|
|
|
|
uurb->buffer_length);
|
2012-07-04 15:18:03 +08:00
|
|
|
}
|
2009-06-29 23:04:54 +08:00
|
|
|
}
|
2008-01-31 07:21:33 +08:00
|
|
|
as->urb->dev = ps->dev;
|
|
|
|
as->urb->pipe = (uurb->type << 30) |
|
2007-07-31 05:09:28 +08:00
|
|
|
__create_pipe(ps->dev, uurb->endpoint & 0xf) |
|
|
|
|
(uurb->endpoint & USB_DIR_IN);
|
2008-04-17 22:18:11 +08:00
|
|
|
|
|
|
|
/* This tedious sequence is necessary because the URB_* flags
|
|
|
|
* are internal to the kernel and subject to change, whereas
|
|
|
|
* the USBDEVFS_URB_* flags are a user API and must not be changed.
|
|
|
|
*/
|
|
|
|
u = (is_in ? URB_DIR_IN : URB_DIR_OUT);
|
|
|
|
if (uurb->flags & USBDEVFS_URB_ISO_ASAP)
|
|
|
|
u |= URB_ISO_ASAP;
|
2014-08-01 15:55:20 +08:00
|
|
|
if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in)
|
2008-04-17 22:18:11 +08:00
|
|
|
u |= URB_SHORT_NOT_OK;
|
|
|
|
if (uurb->flags & USBDEVFS_URB_NO_FSBR)
|
|
|
|
u |= URB_NO_FSBR;
|
|
|
|
if (uurb->flags & USBDEVFS_URB_ZERO_PACKET)
|
|
|
|
u |= URB_ZERO_PACKET;
|
|
|
|
if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT)
|
|
|
|
u |= URB_NO_INTERRUPT;
|
|
|
|
as->urb->transfer_flags = u;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
as->urb->transfer_buffer_length = uurb->buffer_length;
|
2008-01-31 07:21:33 +08:00
|
|
|
as->urb->setup_packet = (unsigned char *)dr;
|
2011-11-18 05:41:14 +08:00
|
|
|
dr = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
as->urb->start_frame = uurb->start_frame;
|
2013-10-09 23:19:28 +08:00
|
|
|
as->urb->number_of_packets = number_of_packets;
|
2013-10-09 23:19:29 +08:00
|
|
|
as->urb->stream_id = stream_id;
|
2007-02-27 03:56:14 +08:00
|
|
|
if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
|
|
|
|
ps->dev->speed == USB_SPEED_HIGH)
|
|
|
|
as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
|
|
|
|
else
|
|
|
|
as->urb->interval = ep->desc.bInterval;
|
2008-01-31 07:21:33 +08:00
|
|
|
as->urb->context = as;
|
|
|
|
as->urb->complete = async_completed;
|
2013-10-09 23:19:28 +08:00
|
|
|
for (totlen = u = 0; u < number_of_packets; u++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
as->urb->iso_frame_desc[u].offset = totlen;
|
|
|
|
as->urb->iso_frame_desc[u].length = isopkt[u].length;
|
|
|
|
totlen += isopkt[u].length;
|
|
|
|
}
|
2005-04-19 08:39:33 +08:00
|
|
|
kfree(isopkt);
|
2011-11-18 05:41:14 +08:00
|
|
|
isopkt = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
as->ps = ps;
|
2008-01-31 07:21:33 +08:00
|
|
|
as->userurb = arg;
|
2009-06-29 23:04:54 +08:00
|
|
|
if (is_in && uurb->buffer_length > 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
as->userbuffer = uurb->buffer;
|
|
|
|
else
|
|
|
|
as->userbuffer = NULL;
|
|
|
|
as->signr = uurb->signr;
|
|
|
|
as->ifnum = ifnum;
|
2006-10-02 17:17:28 +08:00
|
|
|
as->pid = get_pid(task_pid(current));
|
2011-09-26 23:45:18 +08:00
|
|
|
as->cred = get_current_cred();
|
2006-06-30 16:55:48 +08:00
|
|
|
security_task_getsecid(current, &as->secid);
|
2009-06-29 23:02:04 +08:00
|
|
|
snoop_urb(ps->dev, as->userurb, as->urb->pipe,
|
2010-01-27 06:07:29 +08:00
|
|
|
as->urb->transfer_buffer_length, 0, SUBMIT,
|
2012-07-04 15:18:03 +08:00
|
|
|
NULL, 0);
|
|
|
|
if (!is_in)
|
|
|
|
snoop_urb_data(as->urb, as->urb->transfer_buffer_length);
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
async_newpending(as);
|
2009-09-01 23:09:56 +08:00
|
|
|
|
|
|
|
if (usb_endpoint_xfer_bulk(&ep->desc)) {
|
|
|
|
spin_lock_irq(&ps->lock);
|
|
|
|
|
|
|
|
/* Not exactly the endpoint address; the direction bit is
|
|
|
|
* shifted to the 0x10 position so that the value will be
|
|
|
|
* between 0 and 31.
|
|
|
|
*/
|
|
|
|
as->bulk_addr = usb_endpoint_num(&ep->desc) |
|
|
|
|
((ep->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
|
|
|
|
>> 3);
|
|
|
|
|
|
|
|
/* If this bulk URB is the start of a new transfer, re-enable
|
|
|
|
* the endpoint. Otherwise mark it as a continuation URB.
|
|
|
|
*/
|
|
|
|
if (uurb->flags & USBDEVFS_URB_BULK_CONTINUATION)
|
|
|
|
as->bulk_status = AS_CONTINUATION;
|
|
|
|
else
|
|
|
|
ps->disabled_bulk_eps &= ~(1 << as->bulk_addr);
|
|
|
|
|
|
|
|
/* Don't accept continuation URBs if the endpoint is
|
|
|
|
* disabled because of an earlier error.
|
|
|
|
*/
|
|
|
|
if (ps->disabled_bulk_eps & (1 << as->bulk_addr))
|
|
|
|
ret = -EREMOTEIO;
|
|
|
|
else
|
|
|
|
ret = usb_submit_urb(as->urb, GFP_ATOMIC);
|
|
|
|
spin_unlock_irq(&ps->lock);
|
|
|
|
} else {
|
|
|
|
ret = usb_submit_urb(as->urb, GFP_KERNEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret) {
|
2008-01-31 07:21:33 +08:00
|
|
|
dev_printk(KERN_DEBUG, &ps->dev->dev,
|
|
|
|
"usbfs: usb_submit_urb returned %d\n", ret);
|
2009-06-29 23:02:04 +08:00
|
|
|
snoop_urb(ps->dev, as->userurb, as->urb->pipe,
|
2010-01-27 06:07:29 +08:00
|
|
|
0, ret, COMPLETE, NULL, 0);
|
2008-01-31 07:21:33 +08:00
|
|
|
async_removepending(as);
|
2011-11-18 05:41:14 +08:00
|
|
|
goto error;
|
2008-01-31 07:21:33 +08:00
|
|
|
}
|
|
|
|
return 0;
|
2011-11-18 05:41:14 +08:00
|
|
|
|
|
|
|
error:
|
|
|
|
kfree(isopkt);
|
|
|
|
kfree(dr);
|
|
|
|
if (as)
|
|
|
|
free_async(as);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_submiturb(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_urb uurb;
|
|
|
|
|
|
|
|
if (copy_from_user(&uurb, arg, sizeof(uurb)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
return proc_do_submiturb(ps, &uurb,
|
|
|
|
(((struct usbdevfs_urb __user *)arg)->iso_frame_desc),
|
|
|
|
arg);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-05-18 20:12:51 +08:00
|
|
|
struct urb *urb;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct async *as;
|
2012-05-18 20:12:51 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-18 20:12:51 +08:00
|
|
|
spin_lock_irqsave(&ps->lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
as = async_getpending(ps, arg);
|
2012-05-18 20:12:51 +08:00
|
|
|
if (!as) {
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2012-05-18 20:12:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
urb = as->urb;
|
|
|
|
usb_get_urb(urb);
|
|
|
|
spin_unlock_irqrestore(&ps->lock, flags);
|
|
|
|
|
|
|
|
usb_kill_urb(urb);
|
|
|
|
usb_put_urb(urb);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int processcompl(struct async *as, void __user * __user *arg)
|
|
|
|
{
|
|
|
|
struct urb *urb = as->urb;
|
|
|
|
struct usbdevfs_urb __user *userurb = as->userurb;
|
|
|
|
void __user *addr = as->userurb;
|
|
|
|
unsigned int i;
|
|
|
|
|
2010-03-07 04:04:03 +08:00
|
|
|
if (as->userbuffer && urb->actual_length) {
|
2012-07-04 15:18:03 +08:00
|
|
|
if (copy_urb_data_to_user(as->userbuffer, urb))
|
2009-06-29 05:34:14 +08:00
|
|
|
goto err_out;
|
2010-03-07 04:04:03 +08:00
|
|
|
}
|
2007-08-25 03:42:52 +08:00
|
|
|
if (put_user(as->status, &userurb->status))
|
2009-06-29 05:34:14 +08:00
|
|
|
goto err_out;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (put_user(urb->actual_length, &userurb->actual_length))
|
2009-06-29 05:34:14 +08:00
|
|
|
goto err_out;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (put_user(urb->error_count, &userurb->error_count))
|
2009-06-29 05:34:14 +08:00
|
|
|
goto err_out;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-07-31 05:09:28 +08:00
|
|
|
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
|
2005-04-19 08:39:26 +08:00
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
if (put_user(urb->iso_frame_desc[i].actual_length,
|
|
|
|
&userurb->iso_frame_desc[i].actual_length))
|
2009-06-29 05:34:14 +08:00
|
|
|
goto err_out;
|
2005-04-19 08:39:26 +08:00
|
|
|
if (put_user(urb->iso_frame_desc[i].status,
|
|
|
|
&userurb->iso_frame_desc[i].status))
|
2009-06-29 05:34:14 +08:00
|
|
|
goto err_out;
|
2005-04-19 08:39:26 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (put_user(addr, (void __user * __user *)arg))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
2009-06-29 05:34:14 +08:00
|
|
|
|
|
|
|
err_out:
|
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static struct async *reap_as(struct usb_dev_state *ps)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-01-31 07:21:33 +08:00
|
|
|
DECLARE_WAITQUEUE(wait, current);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct async *as = NULL;
|
|
|
|
struct usb_device *dev = ps->dev;
|
|
|
|
|
|
|
|
add_wait_queue(&ps->wait, &wait);
|
|
|
|
for (;;) {
|
|
|
|
__set_current_state(TASK_INTERRUPTIBLE);
|
2008-01-31 07:21:33 +08:00
|
|
|
as = async_getcompleted(ps);
|
2015-01-30 00:29:13 +08:00
|
|
|
if (as || !connected(ps))
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
if (signal_pending(current))
|
|
|
|
break;
|
|
|
|
usb_unlock_device(dev);
|
|
|
|
schedule();
|
|
|
|
usb_lock_device(dev);
|
|
|
|
}
|
|
|
|
remove_wait_queue(&ps->wait, &wait);
|
|
|
|
set_current_state(TASK_RUNNING);
|
|
|
|
return as;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct async *as = reap_as(ps);
|
2010-02-17 04:35:07 +08:00
|
|
|
if (as) {
|
|
|
|
int retval = processcompl(as, (void __user * __user *)arg);
|
|
|
|
free_async(as);
|
|
|
|
return retval;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (signal_pending(current))
|
|
|
|
return -EINTR;
|
2015-01-30 00:29:13 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-02-17 04:35:07 +08:00
|
|
|
int retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct async *as;
|
|
|
|
|
2010-02-17 04:35:07 +08:00
|
|
|
as = async_getcompleted(ps);
|
|
|
|
if (as) {
|
|
|
|
retval = processcompl(as, (void __user * __user *)arg);
|
|
|
|
free_async(as);
|
2015-01-30 00:29:13 +08:00
|
|
|
} else {
|
|
|
|
retval = (connected(ps) ? -EAGAIN : -ENODEV);
|
2010-02-17 04:35:07 +08:00
|
|
|
}
|
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_control_compat(struct usb_dev_state *ps,
|
2009-11-14 09:28:05 +08:00
|
|
|
struct usbdevfs_ctrltransfer32 __user *p32)
|
|
|
|
{
|
2013-10-15 03:46:36 +08:00
|
|
|
struct usbdevfs_ctrltransfer __user *p;
|
|
|
|
__u32 udata;
|
|
|
|
p = compat_alloc_user_space(sizeof(*p));
|
|
|
|
if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
|
|
|
|
get_user(udata, &p32->data) ||
|
2009-11-14 09:28:05 +08:00
|
|
|
put_user(compat_ptr(udata), &p->data))
|
|
|
|
return -EFAULT;
|
2013-10-15 03:46:36 +08:00
|
|
|
return proc_control(ps, p);
|
2009-11-14 09:28:05 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_bulk_compat(struct usb_dev_state *ps,
|
2009-11-14 09:28:05 +08:00
|
|
|
struct usbdevfs_bulktransfer32 __user *p32)
|
|
|
|
{
|
2013-10-15 03:46:37 +08:00
|
|
|
struct usbdevfs_bulktransfer __user *p;
|
|
|
|
compat_uint_t n;
|
|
|
|
compat_caddr_t addr;
|
2009-11-14 09:28:05 +08:00
|
|
|
|
2013-10-15 03:46:37 +08:00
|
|
|
p = compat_alloc_user_space(sizeof(*p));
|
2009-11-14 09:28:05 +08:00
|
|
|
|
2013-10-15 03:46:37 +08:00
|
|
|
if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
|
|
|
|
get_user(n, &p32->len) || put_user(n, &p->len) ||
|
|
|
|
get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
|
|
|
|
get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
|
|
|
|
return -EFAULT;
|
2009-11-14 09:28:05 +08:00
|
|
|
|
2013-10-15 03:46:37 +08:00
|
|
|
return proc_bulk(ps, p);
|
2009-11-14 09:28:05 +08:00
|
|
|
}
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_disconnectsignal_compat(struct usb_dev_state *ps, void __user *arg)
|
2009-11-14 09:28:05 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_disconnectsignal32 ds;
|
|
|
|
|
|
|
|
if (copy_from_user(&ds, arg, sizeof(ds)))
|
|
|
|
return -EFAULT;
|
|
|
|
ps->discsignr = ds.signr;
|
|
|
|
ps->disccontext = compat_ptr(ds.context);
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static int get_urb32(struct usbdevfs_urb *kurb,
|
|
|
|
struct usbdevfs_urb32 __user *uurb)
|
|
|
|
{
|
|
|
|
__u32 uptr;
|
2009-07-29 17:39:03 +08:00
|
|
|
if (!access_ok(VERIFY_READ, uurb, sizeof(*uurb)) ||
|
|
|
|
__get_user(kurb->type, &uurb->type) ||
|
2005-04-17 06:20:36 +08:00
|
|
|
__get_user(kurb->endpoint, &uurb->endpoint) ||
|
|
|
|
__get_user(kurb->status, &uurb->status) ||
|
|
|
|
__get_user(kurb->flags, &uurb->flags) ||
|
|
|
|
__get_user(kurb->buffer_length, &uurb->buffer_length) ||
|
|
|
|
__get_user(kurb->actual_length, &uurb->actual_length) ||
|
|
|
|
__get_user(kurb->start_frame, &uurb->start_frame) ||
|
|
|
|
__get_user(kurb->number_of_packets, &uurb->number_of_packets) ||
|
|
|
|
__get_user(kurb->error_count, &uurb->error_count) ||
|
|
|
|
__get_user(kurb->signr, &uurb->signr))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (__get_user(uptr, &uurb->buffer))
|
|
|
|
return -EFAULT;
|
|
|
|
kurb->buffer = compat_ptr(uptr);
|
2009-01-02 15:48:24 +08:00
|
|
|
if (__get_user(uptr, &uurb->usercontext))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
kurb->usercontext = compat_ptr(uptr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_submiturb_compat(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_urb uurb;
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
if (get_urb32(&uurb, (struct usbdevfs_urb32 __user *)arg))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
return proc_do_submiturb(ps, &uurb,
|
|
|
|
((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc,
|
|
|
|
arg);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int processcompl_compat(struct async *as, void __user * __user *arg)
|
|
|
|
{
|
|
|
|
struct urb *urb = as->urb;
|
|
|
|
struct usbdevfs_urb32 __user *userurb = as->userurb;
|
|
|
|
void __user *addr = as->userurb;
|
|
|
|
unsigned int i;
|
|
|
|
|
2012-07-04 15:18:01 +08:00
|
|
|
if (as->userbuffer && urb->actual_length) {
|
2012-07-04 15:18:03 +08:00
|
|
|
if (copy_urb_data_to_user(as->userbuffer, urb))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
2012-07-04 15:18:01 +08:00
|
|
|
}
|
2007-08-25 03:42:52 +08:00
|
|
|
if (put_user(as->status, &userurb->status))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
if (put_user(urb->actual_length, &userurb->actual_length))
|
|
|
|
return -EFAULT;
|
|
|
|
if (put_user(urb->error_count, &userurb->error_count))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2007-07-31 05:09:28 +08:00
|
|
|
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
|
2005-04-19 08:39:26 +08:00
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
if (put_user(urb->iso_frame_desc[i].actual_length,
|
|
|
|
&userurb->iso_frame_desc[i].actual_length))
|
|
|
|
return -EFAULT;
|
|
|
|
if (put_user(urb->iso_frame_desc[i].status,
|
|
|
|
&userurb->iso_frame_desc[i].status))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-10-11 05:45:37 +08:00
|
|
|
if (put_user(ptr_to_compat(addr), (u32 __user *)arg))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct async *as = reap_as(ps);
|
2010-02-17 04:35:07 +08:00
|
|
|
if (as) {
|
|
|
|
int retval = processcompl_compat(as, (void __user * __user *)arg);
|
|
|
|
free_async(as);
|
|
|
|
return retval;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (signal_pending(current))
|
|
|
|
return -EINTR;
|
2015-01-30 00:29:13 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-02-17 04:35:07 +08:00
|
|
|
int retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct async *as;
|
|
|
|
|
2010-02-17 04:35:07 +08:00
|
|
|
as = async_getcompleted(ps);
|
|
|
|
if (as) {
|
|
|
|
retval = processcompl_compat(as, (void __user * __user *)arg);
|
|
|
|
free_async(as);
|
2015-01-30 00:29:13 +08:00
|
|
|
} else {
|
|
|
|
retval = (connected(ps) ? -EAGAIN : -ENODEV);
|
2010-02-17 04:35:07 +08:00
|
|
|
}
|
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-11-14 09:28:05 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_disconnectsignal(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_disconnectsignal ds;
|
|
|
|
|
|
|
|
if (copy_from_user(&ds, arg, sizeof(ds)))
|
|
|
|
return -EFAULT;
|
|
|
|
ps->discsignr = ds.signr;
|
|
|
|
ps->disccontext = ds.context;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_claiminterface(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned int ifnum;
|
|
|
|
|
|
|
|
if (get_user(ifnum, (unsigned int __user *)arg))
|
|
|
|
return -EFAULT;
|
|
|
|
return claimintf(ps, ifnum);
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned int ifnum;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (get_user(ifnum, (unsigned int __user *)arg))
|
|
|
|
return -EFAULT;
|
|
|
|
if ((ret = releaseintf(ps, ifnum)) < 0)
|
|
|
|
return ret;
|
|
|
|
destroy_async_on_interface (ps, ifnum);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int size;
|
|
|
|
void *buf = NULL;
|
|
|
|
int retval = 0;
|
|
|
|
struct usb_interface *intf = NULL;
|
|
|
|
struct usb_driver *driver = NULL;
|
|
|
|
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
/* alloc buffer */
|
2008-01-31 07:21:33 +08:00
|
|
|
if ((size = _IOC_SIZE(ctl->ioctl_code)) > 0) {
|
2013-05-17 15:13:58 +08:00
|
|
|
buf = kmalloc(size, GFP_KERNEL);
|
|
|
|
if (buf == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) {
|
2008-01-31 07:21:33 +08:00
|
|
|
if (copy_from_user(buf, ctl->data, size)) {
|
2005-04-19 08:39:33 +08:00
|
|
|
kfree(buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
} else {
|
2008-01-31 07:21:33 +08:00
|
|
|
memset(buf, 0, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-02 10:05:56 +08:00
|
|
|
if (!connected(ps)) {
|
2005-04-19 08:39:33 +08:00
|
|
|
kfree(buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ps->dev->state != USB_STATE_CONFIGURED)
|
|
|
|
retval = -EHOSTUNREACH;
|
2008-01-31 07:21:33 +08:00
|
|
|
else if (!(intf = usb_ifnum_to_if(ps->dev, ctl->ifno)))
|
|
|
|
retval = -EINVAL;
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
else switch (ctl->ioctl_code) {
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* disconnect kernel driver from interface */
|
|
|
|
case USBDEVFS_DISCONNECT:
|
|
|
|
if (intf->dev.driver) {
|
|
|
|
driver = to_usb_driver(intf->dev.driver);
|
2008-01-31 07:21:33 +08:00
|
|
|
dev_dbg(&intf->dev, "disconnect by usbfs\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_driver_release_interface(driver, intf);
|
|
|
|
} else
|
|
|
|
retval = -ENODATA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* let kernel drivers try to (re)bind to the interface */
|
|
|
|
case USBDEVFS_CONNECT:
|
2007-12-04 04:42:10 +08:00
|
|
|
if (!intf->dev.driver)
|
|
|
|
retval = device_attach(&intf->dev);
|
|
|
|
else
|
|
|
|
retval = -EBUSY;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* talk directly to the interface's driver */
|
|
|
|
default:
|
|
|
|
if (intf->dev.driver)
|
|
|
|
driver = to_usb_driver(intf->dev.driver);
|
2010-06-02 05:04:41 +08:00
|
|
|
if (driver == NULL || driver->unlocked_ioctl == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -ENOTTY;
|
|
|
|
} else {
|
2010-06-02 05:04:41 +08:00
|
|
|
retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval == -ENOIOCTLCMD)
|
|
|
|
retval = -ENOTTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* cleanup and return */
|
|
|
|
if (retval >= 0
|
2008-01-31 07:21:33 +08:00
|
|
|
&& (_IOC_DIR(ctl->ioctl_code) & _IOC_READ) != 0
|
2005-04-17 06:20:36 +08:00
|
|
|
&& size > 0
|
2008-01-31 07:21:33 +08:00
|
|
|
&& copy_to_user(ctl->data, buf, size) != 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -EFAULT;
|
2005-04-19 08:39:33 +08:00
|
|
|
|
|
|
|
kfree(buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_ioctl_default(struct usb_dev_state *ps, void __user *arg)
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_ioctl ctrl;
|
|
|
|
|
2008-01-31 07:21:33 +08:00
|
|
|
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
return -EFAULT;
|
|
|
|
return proc_ioctl(ps, &ctrl);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_ioctl_compat(struct usb_dev_state *ps, compat_uptr_t arg)
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_ioctl32 __user *uioc;
|
|
|
|
struct usbdevfs_ioctl ctrl;
|
|
|
|
u32 udata;
|
|
|
|
|
2005-11-18 01:47:49 +08:00
|
|
|
uioc = compat_ptr((long)arg);
|
2009-07-29 17:39:03 +08:00
|
|
|
if (!access_ok(VERIFY_READ, uioc, sizeof(*uioc)) ||
|
|
|
|
__get_user(ctrl.ifno, &uioc->ifno) ||
|
|
|
|
__get_user(ctrl.ioctl_code, &uioc->ioctl_code) ||
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
__get_user(udata, &uioc->data))
|
|
|
|
return -EFAULT;
|
|
|
|
ctrl.data = compat_ptr(udata);
|
|
|
|
|
|
|
|
return proc_ioctl(ps, &ctrl);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_claim_port(struct usb_dev_state *ps, void __user *arg)
|
2009-06-29 22:56:54 +08:00
|
|
|
{
|
|
|
|
unsigned portnum;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (get_user(portnum, (unsigned __user *) arg))
|
|
|
|
return -EFAULT;
|
|
|
|
rc = usb_hub_claim_port(ps->dev, portnum, ps);
|
|
|
|
if (rc == 0)
|
|
|
|
snoop(&ps->dev->dev, "port %d claimed by process %d: %s\n",
|
|
|
|
portnum, task_pid_nr(current), current->comm);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_release_port(struct usb_dev_state *ps, void __user *arg)
|
2009-06-29 22:56:54 +08:00
|
|
|
{
|
|
|
|
unsigned portnum;
|
|
|
|
|
|
|
|
if (get_user(portnum, (unsigned __user *) arg))
|
|
|
|
return -EFAULT;
|
|
|
|
return usb_hub_release_port(ps->dev, portnum, ps);
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
|
usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl
There are a few (new) usbdevfs capabilities which an application cannot
discover in any other way then checking the kernel version. There are 3
problems with this:
1) It is just not very pretty.
2) Given the tendency of enterprise distros to backport stuff it is not
reliable.
3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION
does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK
(which is its intended use) on devices attached to an XHCI controller.
So the availability of these features can be host controller dependent,
making depending on them based on the kernel version not a good idea.
This patch besides adding the new ioctl also adds flags for the following
existing capabilities:
USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31
USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI
USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3
Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION
cap for XHCI controllers, bulk transfers with this flag set will still be
accepted when submitted to XHCI controllers.
Returning -EINVAL for them would break existing apps, and in most cases the
troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers
will never get hit, so this would break working use cases.
The disadvantage of not returning -EINVAL is that cases were it is causing
real trouble may go undetected / the cause of the trouble may be unclear,
but this is the best we can do.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-04 15:18:02 +08:00
|
|
|
{
|
|
|
|
__u32 caps;
|
|
|
|
|
2015-01-30 00:29:13 +08:00
|
|
|
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
|
|
|
|
USBDEVFS_CAP_REAP_AFTER_DISCONNECT;
|
usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl
There are a few (new) usbdevfs capabilities which an application cannot
discover in any other way then checking the kernel version. There are 3
problems with this:
1) It is just not very pretty.
2) Given the tendency of enterprise distros to backport stuff it is not
reliable.
3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION
does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK
(which is its intended use) on devices attached to an XHCI controller.
So the availability of these features can be host controller dependent,
making depending on them based on the kernel version not a good idea.
This patch besides adding the new ioctl also adds flags for the following
existing capabilities:
USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31
USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI
USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3
Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION
cap for XHCI controllers, bulk transfers with this flag set will still be
accepted when submitted to XHCI controllers.
Returning -EINVAL for them would break existing apps, and in most cases the
troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers
will never get hit, so this would break working use cases.
The disadvantage of not returning -EINVAL is that cases were it is causing
real trouble may go undetected / the cause of the trouble may be unclear,
but this is the best we can do.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-04 15:18:02 +08:00
|
|
|
if (!ps->dev->bus->no_stop_on_short)
|
|
|
|
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
|
2012-07-04 15:18:03 +08:00
|
|
|
if (ps->dev->bus->sg_tablesize)
|
|
|
|
caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER;
|
usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl
There are a few (new) usbdevfs capabilities which an application cannot
discover in any other way then checking the kernel version. There are 3
problems with this:
1) It is just not very pretty.
2) Given the tendency of enterprise distros to backport stuff it is not
reliable.
3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION
does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK
(which is its intended use) on devices attached to an XHCI controller.
So the availability of these features can be host controller dependent,
making depending on them based on the kernel version not a good idea.
This patch besides adding the new ioctl also adds flags for the following
existing capabilities:
USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31
USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI
USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3
Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION
cap for XHCI controllers, bulk transfers with this flag set will still be
accepted when submitted to XHCI controllers.
Returning -EINVAL for them would break existing apps, and in most cases the
troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers
will never get hit, so this would break working use cases.
The disadvantage of not returning -EINVAL is that cases were it is causing
real trouble may go undetected / the cause of the trouble may be unclear,
but this is the best we can do.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-04 15:18:02 +08:00
|
|
|
|
|
|
|
if (put_user(caps, (__u32 __user *)arg))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-10 16:36:40 +08:00
|
|
|
static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
|
2012-09-09 02:02:05 +08:00
|
|
|
{
|
|
|
|
struct usbdevfs_disconnect_claim dc;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
|
|
|
|
if (copy_from_user(&dc, arg, sizeof(dc)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
intf = usb_ifnum_to_if(ps->dev, dc.interface);
|
|
|
|
if (!intf)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (intf->dev.driver) {
|
|
|
|
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
|
|
|
|
|
|
|
|
if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
|
|
|
|
strncmp(dc.driver, intf->dev.driver->name,
|
|
|
|
sizeof(dc.driver)) != 0)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) &&
|
|
|
|
strncmp(dc.driver, intf->dev.driver->name,
|
|
|
|
sizeof(dc.driver)) == 0)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
dev_dbg(&intf->dev, "disconnect by usbfs\n");
|
|
|
|
usb_driver_release_interface(driver, intf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return claimintf(ps, dc.interface);
|
|
|
|
}
|
|
|
|
|
2014-04-02 08:06:09 +08:00
|
|
|
static int proc_alloc_streams(struct usb_dev_state *ps, void __user *arg)
|
2013-10-09 23:19:31 +08:00
|
|
|
{
|
|
|
|
unsigned num_streams, num_eps;
|
|
|
|
struct usb_host_endpoint **eps;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps,
|
|
|
|
&eps, &intf);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
destroy_async_on_interface(ps,
|
|
|
|
intf->altsetting[0].desc.bInterfaceNumber);
|
|
|
|
|
|
|
|
r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL);
|
|
|
|
kfree(eps);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-04-02 08:06:09 +08:00
|
|
|
static int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
|
2013-10-09 23:19:31 +08:00
|
|
|
{
|
|
|
|
unsigned num_eps;
|
|
|
|
struct usb_host_endpoint **eps;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
destroy_async_on_interface(ps,
|
|
|
|
intf->altsetting[0].desc.bInterfaceNumber);
|
|
|
|
|
|
|
|
r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL);
|
|
|
|
kfree(eps);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* NOTE: All requests here that have interface numbers as parameters
|
|
|
|
* are assuming that somehow the configuration has been prevented from
|
|
|
|
* changing. But there's no mechanism to ensure that...
|
|
|
|
*/
|
2009-11-14 09:28:05 +08:00
|
|
|
static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
void __user *p)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = file->private_data;
|
2013-01-24 06:07:38 +08:00
|
|
|
struct inode *inode = file_inode(file);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct usb_device *dev = ps->dev;
|
|
|
|
int ret = -ENOTTY;
|
|
|
|
|
|
|
|
if (!(file->f_mode & FMODE_WRITE))
|
|
|
|
return -EPERM;
|
2010-01-13 22:33:43 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_lock_device(dev);
|
2015-01-30 00:29:13 +08:00
|
|
|
|
|
|
|
/* Reap operations are allowed even after disconnection */
|
|
|
|
switch (cmd) {
|
|
|
|
case USBDEVFS_REAPURB:
|
|
|
|
snoop(&dev->dev, "%s: REAPURB\n", __func__);
|
|
|
|
ret = proc_reapurb(ps, p);
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
case USBDEVFS_REAPURBNDELAY:
|
|
|
|
snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
|
|
|
|
ret = proc_reapurbnonblock(ps, p);
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
case USBDEVFS_REAPURB32:
|
|
|
|
snoop(&dev->dev, "%s: REAPURB32\n", __func__);
|
|
|
|
ret = proc_reapurb_compat(ps, p);
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
case USBDEVFS_REAPURBNDELAY32:
|
|
|
|
snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
|
|
|
|
ret = proc_reapurbnonblock_compat(ps, p);
|
|
|
|
goto done;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2006-07-02 10:05:56 +08:00
|
|
|
if (!connected(ps)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_unlock_device(dev);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case USBDEVFS_CONTROL:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: CONTROL\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_control(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_BULK:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: BULK\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_bulk(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_RESETEP:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: RESETEP\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_resetep(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_RESET:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: RESET\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_resetdevice(ps);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_CLEAR_HALT:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: CLEAR_HALT\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_clearhalt(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_GETDRIVER:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: GETDRIVER\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_getdriver(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_CONNECTINFO:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: CONNECTINFO\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_connectinfo(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_SETINTERFACE:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: SETINTERFACE\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_setintf(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_SETCONFIGURATION:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: SETCONFIGURATION\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_setconfig(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_SUBMITURB:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: SUBMITURB\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_submiturb(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
2009-11-14 09:28:05 +08:00
|
|
|
case USBDEVFS_CONTROL32:
|
|
|
|
snoop(&dev->dev, "%s: CONTROL32\n", __func__);
|
|
|
|
ret = proc_control_compat(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_BULK32:
|
|
|
|
snoop(&dev->dev, "%s: BULK32\n", __func__);
|
|
|
|
ret = proc_bulk_compat(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_DISCSIGNAL32:
|
|
|
|
snoop(&dev->dev, "%s: DISCSIGNAL32\n", __func__);
|
|
|
|
ret = proc_disconnectsignal_compat(ps, p);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
case USBDEVFS_SUBMITURB32:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: SUBMITURB32\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_submiturb_compat(ps, p);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_mtime = CURRENT_TIME;
|
|
|
|
break;
|
|
|
|
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
case USBDEVFS_IOCTL32:
|
2009-11-14 09:28:05 +08:00
|
|
|
snoop(&dev->dev, "%s: IOCTL32\n", __func__);
|
2006-10-11 05:45:37 +08:00
|
|
|
ret = proc_ioctl_compat(ps, ptr_to_compat(p));
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
case USBDEVFS_DISCARDURB:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: DISCARDURB\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_unlinkurb(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_DISCSIGNAL:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_disconnectsignal(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_CLAIMINTERFACE:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: CLAIMINTERFACE\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_claiminterface(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_RELEASEINTERFACE:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: RELEASEINTERFACE\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = proc_releaseinterface(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_IOCTL:
|
2008-03-04 08:08:34 +08:00
|
|
|
snoop(&dev->dev, "%s: IOCTL\n", __func__);
|
[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs
Dell supplied me with the following test:
#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>
main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}
I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-10-18 09:15:54 +08:00
|
|
|
ret = proc_ioctl_default(ps, p);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2009-06-29 22:56:54 +08:00
|
|
|
|
|
|
|
case USBDEVFS_CLAIM_PORT:
|
|
|
|
snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__);
|
|
|
|
ret = proc_claim_port(ps, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBDEVFS_RELEASE_PORT:
|
|
|
|
snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
|
|
|
|
ret = proc_release_port(ps, p);
|
|
|
|
break;
|
usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl
There are a few (new) usbdevfs capabilities which an application cannot
discover in any other way then checking the kernel version. There are 3
problems with this:
1) It is just not very pretty.
2) Given the tendency of enterprise distros to backport stuff it is not
reliable.
3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION
does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK
(which is its intended use) on devices attached to an XHCI controller.
So the availability of these features can be host controller dependent,
making depending on them based on the kernel version not a good idea.
This patch besides adding the new ioctl also adds flags for the following
existing capabilities:
USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31
USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI
USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3
Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION
cap for XHCI controllers, bulk transfers with this flag set will still be
accepted when submitted to XHCI controllers.
Returning -EINVAL for them would break existing apps, and in most cases the
troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers
will never get hit, so this would break working use cases.
The disadvantage of not returning -EINVAL is that cases were it is causing
real trouble may go undetected / the cause of the trouble may be unclear,
but this is the best we can do.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-07-04 15:18:02 +08:00
|
|
|
case USBDEVFS_GET_CAPABILITIES:
|
|
|
|
ret = proc_get_capabilities(ps, p);
|
|
|
|
break;
|
2012-09-09 02:02:05 +08:00
|
|
|
case USBDEVFS_DISCONNECT_CLAIM:
|
|
|
|
ret = proc_disconnect_claim(ps, p);
|
|
|
|
break;
|
2013-10-09 23:19:31 +08:00
|
|
|
case USBDEVFS_ALLOC_STREAMS:
|
|
|
|
ret = proc_alloc_streams(ps, p);
|
|
|
|
break;
|
|
|
|
case USBDEVFS_FREE_STREAMS:
|
|
|
|
ret = proc_free_streams(ps, p);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2015-01-30 00:29:13 +08:00
|
|
|
|
|
|
|
done:
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_unlock_device(dev);
|
|
|
|
if (ret >= 0)
|
|
|
|
inode->i_atime = CURRENT_TIME;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-11-14 09:28:05 +08:00
|
|
|
static long usbdev_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = usbdev_do_ioctl(file, cmd, (void __user *)arg);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
static long usbdev_compat_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* No kernel lock - fine */
|
2008-01-31 07:21:33 +08:00
|
|
|
static unsigned int usbdev_poll(struct file *file,
|
|
|
|
struct poll_table_struct *wait)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps = file->private_data;
|
2006-09-14 03:38:41 +08:00
|
|
|
unsigned int mask = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
poll_wait(file, &ps->wait, wait);
|
|
|
|
if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
|
|
|
|
mask |= POLLOUT | POLLWRNORM;
|
2006-07-02 10:05:56 +08:00
|
|
|
if (!connected(ps))
|
2005-04-17 06:20:36 +08:00
|
|
|
mask |= POLLERR | POLLHUP;
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:59:31 +08:00
|
|
|
const struct file_operations usbdev_file_operations = {
|
2009-11-14 09:28:05 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.llseek = usbdev_lseek,
|
|
|
|
.read = usbdev_read,
|
|
|
|
.poll = usbdev_poll,
|
|
|
|
.unlocked_ioctl = usbdev_ioctl,
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = usbdev_compat_ioctl,
|
|
|
|
#endif
|
|
|
|
.open = usbdev_open,
|
|
|
|
.release = usbdev_release,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
2005-07-31 07:05:53 +08:00
|
|
|
|
2009-01-14 00:33:42 +08:00
|
|
|
static void usbdev_remove(struct usb_device *udev)
|
2008-06-25 02:47:04 +08:00
|
|
|
{
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *ps;
|
2008-06-25 02:47:04 +08:00
|
|
|
struct siginfo sinfo;
|
|
|
|
|
|
|
|
while (!list_empty(&udev->filelist)) {
|
2014-03-10 16:36:40 +08:00
|
|
|
ps = list_entry(udev->filelist.next, struct usb_dev_state, list);
|
2008-06-25 02:47:04 +08:00
|
|
|
destroy_all_async(ps);
|
|
|
|
wake_up_all(&ps->wait);
|
|
|
|
list_del_init(&ps->list);
|
|
|
|
if (ps->discsignr) {
|
|
|
|
sinfo.si_signo = ps->discsignr;
|
|
|
|
sinfo.si_errno = EPIPE;
|
|
|
|
sinfo.si_code = SI_ASYNCIO;
|
|
|
|
sinfo.si_addr = ps->disccontext;
|
2011-09-26 23:45:18 +08:00
|
|
|
kill_pid_info_as_cred(ps->discsignr, &sinfo,
|
|
|
|
ps->disc_pid, ps->cred, ps->secid);
|
2008-06-25 02:47:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-14 00:33:42 +08:00
|
|
|
static int usbdev_notify(struct notifier_block *self,
|
2007-03-13 22:59:31 +08:00
|
|
|
unsigned long action, void *dev)
|
2005-06-21 12:15:16 +08:00
|
|
|
{
|
|
|
|
switch (action) {
|
|
|
|
case USB_DEVICE_ADD:
|
|
|
|
break;
|
|
|
|
case USB_DEVICE_REMOVE:
|
2009-01-14 00:33:42 +08:00
|
|
|
usbdev_remove(dev);
|
2005-06-21 12:15:16 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block usbdev_nb = {
|
2009-01-14 00:33:42 +08:00
|
|
|
.notifier_call = usbdev_notify,
|
2005-06-21 12:15:16 +08:00
|
|
|
};
|
|
|
|
|
2007-09-13 06:06:57 +08:00
|
|
|
static struct cdev usb_device_cdev;
|
2005-07-31 07:05:53 +08:00
|
|
|
|
2007-03-13 22:59:31 +08:00
|
|
|
int __init usb_devio_init(void)
|
2005-07-31 07:05:53 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
2005-08-11 03:15:57 +08:00
|
|
|
retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
|
2008-01-31 07:21:33 +08:00
|
|
|
"usb_device");
|
2005-07-31 07:05:53 +08:00
|
|
|
if (retval) {
|
2008-08-15 00:37:34 +08:00
|
|
|
printk(KERN_ERR "Unable to register minors for usb_device\n");
|
2005-07-31 07:05:53 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2007-03-13 22:59:31 +08:00
|
|
|
cdev_init(&usb_device_cdev, &usbdev_file_operations);
|
2005-08-11 03:15:57 +08:00
|
|
|
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
|
2005-07-31 07:05:53 +08:00
|
|
|
if (retval) {
|
2008-08-15 00:37:34 +08:00
|
|
|
printk(KERN_ERR "Unable to get usb_device major %d\n",
|
|
|
|
USB_DEVICE_MAJOR);
|
2005-06-21 12:15:16 +08:00
|
|
|
goto error_cdev;
|
2005-07-31 07:05:53 +08:00
|
|
|
}
|
2009-01-14 00:33:42 +08:00
|
|
|
usb_register_notify(&usbdev_nb);
|
2005-07-31 07:05:53 +08:00
|
|
|
out:
|
|
|
|
return retval;
|
2005-06-21 12:15:16 +08:00
|
|
|
|
|
|
|
error_cdev:
|
|
|
|
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
|
|
|
goto out;
|
2005-07-31 07:05:53 +08:00
|
|
|
}
|
|
|
|
|
2007-03-13 22:59:31 +08:00
|
|
|
void usb_devio_cleanup(void)
|
2005-07-31 07:05:53 +08:00
|
|
|
{
|
2005-06-21 12:15:16 +08:00
|
|
|
usb_unregister_notify(&usbdev_nb);
|
2005-07-31 07:05:53 +08:00
|
|
|
cdev_del(&usb_device_cdev);
|
2005-08-11 03:15:57 +08:00
|
|
|
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
2005-07-31 07:05:53 +08:00
|
|
|
}
|