aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/devio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/devio.c')
-rw-r--r--drivers/usb/core/devio.c123
1 files changed, 76 insertions, 47 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index fa783531ee88..b265ab5405f9 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -48,9 +48,6 @@
#define USB_DEVICE_MAX (USB_MAXBUS * 128)
#define USB_SG_SIZE 16384 /* split-size for large txs */
-/* Mutual exclusion for removal, open, and release */
-DEFINE_MUTEX(usbfs_mutex);
-
struct usb_dev_state {
struct list_head list; /* state list */
struct usb_device *dev;
@@ -63,7 +60,7 @@ struct usb_dev_state {
unsigned int discsignr;
struct pid *disc_pid;
const struct cred *cred;
- void __user *disccontext;
+ sigval_t disccontext;
unsigned long ifclaimed;
u32 disabled_bulk_eps;
bool privileges_dropped;
@@ -90,6 +87,7 @@ struct async {
unsigned int ifnum;
void __user *userbuffer;
void __user *userurb;
+ sigval_t userurb_sigval;
struct urb *urb;
struct usb_memory *usbm;
unsigned int mem_usage;
@@ -582,22 +580,19 @@ static void async_completed(struct urb *urb)
{
struct async *as = urb->context;
struct usb_dev_state *ps = as->ps;
- struct kernel_siginfo sinfo;
struct pid *pid = NULL;
const struct cred *cred = NULL;
unsigned long flags;
- int signr;
+ sigval_t addr;
+ int signr, errno;
spin_lock_irqsave(&ps->lock, flags);
list_move_tail(&as->asynclist, &ps->async_completed);
as->status = urb->status;
signr = as->signr;
if (signr) {
- clear_siginfo(&sinfo);
- sinfo.si_signo = as->signr;
- sinfo.si_errno = as->status;
- sinfo.si_code = SI_ASYNCIO;
- sinfo.si_addr = as->userurb;
+ errno = as->status;
+ addr = as->userurb_sigval;
pid = get_pid(as->pid);
cred = get_cred(as->cred);
}
@@ -615,7 +610,7 @@ static void async_completed(struct urb *urb)
spin_unlock_irqrestore(&ps->lock, flags);
if (signr) {
- kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred);
+ kill_pid_usb_asyncio(signr, errno, addr, pid, cred);
put_pid(pid);
put_cred(cred);
}
@@ -947,9 +942,9 @@ error:
return ret;
}
-static int match_devt(struct device *dev, void *data)
+static int match_devt(struct device *dev, const void *data)
{
- return dev->devt == (dev_t) (unsigned long) data;
+ return dev->devt == (dev_t)(unsigned long)(void *)data;
}
static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
@@ -979,15 +974,9 @@ static int usbdev_open(struct inode *inode, struct file *file)
ret = -ENODEV;
- /* Protect against simultaneous removal or release */
- mutex_lock(&usbfs_mutex);
-
/* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
dev = usbdev_lookup_by_devt(inode->i_rdev);
-
- mutex_unlock(&usbfs_mutex);
-
if (!dev)
goto out_free_ps;
@@ -1308,6 +1297,39 @@ static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
return 0;
}
+static int proc_conninfo_ex(struct usb_dev_state *ps,
+ void __user *arg, size_t size)
+{
+ struct usbdevfs_conninfo_ex ci;
+ struct usb_device *udev = ps->dev;
+
+ if (size < sizeof(ci.size))
+ return -EINVAL;
+
+ memset(&ci, 0, sizeof(ci));
+ ci.size = sizeof(ci);
+ ci.busnum = udev->bus->busnum;
+ ci.devnum = udev->devnum;
+ ci.speed = udev->speed;
+
+ while (udev && udev->portnum != 0) {
+ if (++ci.num_ports <= ARRAY_SIZE(ci.ports))
+ ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports] =
+ udev->portnum;
+ udev = udev->parent;
+ }
+
+ if (ci.num_ports < ARRAY_SIZE(ci.ports))
+ memmove(&ci.ports[0],
+ &ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports],
+ ci.num_ports);
+
+ if (copy_to_user(arg, &ci, min(sizeof(ci), size)))
+ return -EFAULT;
+
+ return 0;
+}
+
static int proc_resetdevice(struct usb_dev_state *ps)
{
struct usb_host_config *actconfig = ps->dev->actconfig;
@@ -1427,7 +1449,7 @@ find_memory_area(struct usb_dev_state *ps, const struct usbdevfs_urb *uurb)
static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,
struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
- void __user *arg)
+ void __user *arg, sigval_t userurb_sigval)
{
struct usbdevfs_iso_packet_desc *isopkt = NULL;
struct usb_host_endpoint *ep;
@@ -1486,15 +1508,15 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
ret = -EFAULT;
goto error;
}
- if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) {
+ if (uurb->buffer_length < (le16_to_cpu(dr->wLength) + 8)) {
ret = -EINVAL;
goto error;
}
ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
- le16_to_cpup(&dr->wIndex));
+ le16_to_cpu(dr->wIndex));
if (ret)
goto error;
- uurb->buffer_length = le16_to_cpup(&dr->wLength);
+ uurb->buffer_length = le16_to_cpu(dr->wLength);
uurb->buffer += 8;
if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
is_in = 1;
@@ -1509,9 +1531,9 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
"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));
+ __le16_to_cpu(dr->wValue),
+ __le16_to_cpu(dr->wIndex),
+ __le16_to_cpu(dr->wLength));
u = sizeof(struct usb_ctrlrequest);
break;
@@ -1727,6 +1749,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
isopkt = NULL;
as->ps = ps;
as->userurb = arg;
+ as->userurb_sigval = userurb_sigval;
if (as->usbm) {
unsigned long uurb_start = (unsigned long)uurb->buffer;
@@ -1801,13 +1824,17 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
static int proc_submiturb(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_urb uurb;
+ sigval_t userurb_sigval;
if (copy_from_user(&uurb, arg, sizeof(uurb)))
return -EFAULT;
+ memset(&userurb_sigval, 0, sizeof(userurb_sigval));
+ userurb_sigval.sival_ptr = arg;
+
return proc_do_submiturb(ps, &uurb,
(((struct usbdevfs_urb __user *)arg)->iso_frame_desc),
- arg);
+ arg, userurb_sigval);
}
static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg)
@@ -1977,7 +2004,7 @@ static int proc_disconnectsignal_compat(struct usb_dev_state *ps, void __user *a
if (copy_from_user(&ds, arg, sizeof(ds)))
return -EFAULT;
ps->discsignr = ds.signr;
- ps->disccontext = compat_ptr(ds.context);
+ ps->disccontext.sival_int = ds.context;
return 0;
}
@@ -2005,13 +2032,17 @@ static int get_urb32(struct usbdevfs_urb *kurb,
static int proc_submiturb_compat(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_urb uurb;
+ sigval_t userurb_sigval;
if (get_urb32(&uurb, (struct usbdevfs_urb32 __user *)arg))
return -EFAULT;
+ memset(&userurb_sigval, 0, sizeof(userurb_sigval));
+ userurb_sigval.sival_int = ptr_to_compat(arg);
+
return proc_do_submiturb(ps, &uurb,
((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc,
- arg);
+ arg, userurb_sigval);
}
static int processcompl_compat(struct async *as, void __user * __user *arg)
@@ -2092,7 +2123,7 @@ static int proc_disconnectsignal(struct usb_dev_state *ps, void __user *arg)
if (copy_from_user(&ds, arg, sizeof(ds)))
return -EFAULT;
ps->discsignr = ds.signr;
- ps->disccontext = ds.context;
+ ps->disccontext.sival_ptr = ds.context;
return 0;
}
@@ -2130,6 +2161,9 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
if (ps->privileges_dropped)
return -EACCES;
+ if (!connected(ps))
+ return -ENODEV;
+
/* alloc buffer */
size = _IOC_SIZE(ctl->ioctl_code);
if (size > 0) {
@@ -2146,11 +2180,6 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
}
}
- if (!connected(ps)) {
- kfree(buf);
- return -ENODEV;
- }
-
if (ps->dev->state != USB_STATE_CONFIGURED)
retval = -EHOSTUNREACH;
else if (!(intf = usb_ifnum_to_if(ps->dev, ctl->ifno)))
@@ -2252,7 +2281,7 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
- USBDEVFS_CAP_DROP_PRIVILEGES;
+ USBDEVFS_CAP_DROP_PRIVILEGES | USBDEVFS_CAP_CONNINFO_EX;
if (!ps->dev->bus->no_stop_on_short)
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
if (ps->dev->bus->sg_tablesize)
@@ -2551,6 +2580,13 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
break;
}
+ /* Handle variable-length commands */
+ switch (cmd & ~IOCSIZE_MASK) {
+ case USBDEVFS_CONNINFO_EX(0):
+ ret = proc_conninfo_ex(ps, p, _IOC_SIZE(cmd));
+ break;
+ }
+
done:
usb_unlock_device(dev);
if (ret >= 0)
@@ -2614,22 +2650,15 @@ const struct file_operations usbdev_file_operations = {
static void usbdev_remove(struct usb_device *udev)
{
struct usb_dev_state *ps;
- struct kernel_siginfo sinfo;
while (!list_empty(&udev->filelist)) {
ps = list_entry(udev->filelist.next, struct usb_dev_state, list);
destroy_all_async(ps);
wake_up_all(&ps->wait);
list_del_init(&ps->list);
- if (ps->discsignr) {
- clear_siginfo(&sinfo);
- sinfo.si_signo = ps->discsignr;
- sinfo.si_errno = EPIPE;
- sinfo.si_code = SI_ASYNCIO;
- sinfo.si_addr = ps->disccontext;
- kill_pid_info_as_cred(ps->discsignr, &sinfo,
- ps->disc_pid, ps->cred);
- }
+ if (ps->discsignr)
+ kill_pid_usb_asyncio(ps->discsignr, EPIPE, ps->disccontext,
+ ps->disc_pid, ps->cred);
}
}