X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fcore%2Fdevio.c;h=6bfab4bcaa9e945a5f919a93ce1cb0f75da08775;hb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;hp=6f969a4da8e9c9e95a9387bf840425a52ab4a2f3;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 6f969a4da..6bfab4bca 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -21,7 +21,7 @@ * * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ * - * This file implements the usbdevfs/x/y files, where + * 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 @@ -72,6 +72,8 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic"); } while (0) +#define MAX_USBFS_BUFFER_SIZE 16384 + static inline int connected (struct usb_device *dev) { return dev->state != USB_STATE_NOTATTACHED; @@ -111,7 +113,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l int i; pos = *ppos; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { ret = -ENODEV; goto err; @@ -121,13 +123,26 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l } if (pos < sizeof(struct usb_device_descriptor)) { + struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto err; + } + memcpy(desc, &dev->descriptor, sizeof(dev->descriptor)); + le16_to_cpus(&desc->bcdUSB); + le16_to_cpus(&desc->idVendor); + le16_to_cpus(&desc->idProduct); + le16_to_cpus(&desc->bcdDevice); + len = sizeof(struct usb_device_descriptor) - pos; if (len > nbytes) len = nbytes; - if (copy_to_user(buf, ((char *)&dev->descriptor) + pos, len)) { + if (copy_to_user(buf, ((char *)desc) + pos, len)) { + kfree(desc); ret = -EFAULT; goto err; } + kfree(desc); *ppos += len; buf += len; @@ -146,7 +161,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l /* The descriptor may claim to be longer than it * really is. Here is the actual allocated length. */ unsigned alloclen = - dev->config[i].desc.wTotalLength; + le16_to_cpu(dev->config[i].desc.wTotalLength); len = length - (*ppos - pos); if (len > nbytes) @@ -173,7 +188,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l } err: - up(&dev->serialize); + usb_unlock_device(dev); return ret; } @@ -198,12 +213,10 @@ static struct async *alloc_async(unsigned int numisoframes) static void free_async(struct async *as) { - if (as->urb->transfer_buffer) - kfree(as->urb->transfer_buffer); - if (as->urb->setup_packet) - kfree(as->urb->setup_packet); + kfree(as->urb->transfer_buffer); + kfree(as->urb->setup_packet); usb_free_urb(as->urb); - kfree(as); + kfree(as); } static inline void async_newpending(struct async *as) @@ -284,9 +297,10 @@ static void destroy_async (struct dev_state *ps, struct list_head *list) while (!list_empty(list)) { as = list_entry(list->next, struct async, asynclist); list_del_init(&as->asynclist); + + /* drop the spinlock so the completion handler can run */ spin_unlock_irqrestore(&ps->lock, flags); - /* usb_unlink_urb calls the completion handler with status == -ENOENT */ - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); spin_lock_irqsave(&ps->lock, flags); } spin_unlock_irqrestore(&ps->lock, flags); @@ -351,7 +365,7 @@ static void driver_disconnect(struct usb_interface *intf) destroy_async_on_interface(ps, ifnum); } -struct usb_driver usbdevfs_driver = { +struct usb_driver usbfs_driver = { .owner = THIS_MODULE, .name = "usbfs", .probe = driver_probe, @@ -376,7 +390,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else - err = usb_driver_claim_interface(&usbdevfs_driver, intf, ps); + err = usb_driver_claim_interface(&usbfs_driver, intf, ps); up_write(&usb_bus_type.subsys.rwsem); if (err == 0) set_bit(ifnum, &ps->ifclaimed); @@ -399,7 +413,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { - usb_driver_release_interface(&usbdevfs_driver, intf); + usb_driver_release_interface(&usbfs_driver, intf); err = 0; } up_write(&usb_bus_type.subsys.rwsem); @@ -408,6 +422,8 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) static int checkintf(struct dev_state *ps, unsigned int ifnum) { + if (ps->dev->state != USB_STATE_CONFIGURED) + return -EHOSTUNREACH; if (ifnum >= 8*sizeof(ps->ifclaimed)) return -EINVAL; if (test_bit(ifnum, &ps->ifclaimed)) @@ -447,6 +463,8 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig { int ret = 0; + if (ps->dev->state != USB_STATE_CONFIGURED) + return -EHOSTUNREACH; if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype)) return 0; @@ -514,16 +532,15 @@ static int usbdev_release(struct inode *inode, struct file *file) struct usb_device *dev = ps->dev; unsigned int ifnum; - down(&dev->serialize); + usb_lock_device(dev); list_del_init(&ps->list); - - if (connected(dev)) { - for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); ifnum++) - if (test_bit(ifnum, &ps->ifclaimed)) - releaseintf(ps, ifnum); - destroy_all_async(ps); + for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); + ifnum++) { + if (test_bit(ifnum, &ps->ifclaimed)) + releaseintf(ps, ifnum); } - up(&dev->serialize); + destroy_all_async(ps); + usb_unlock_device(dev); usb_put_dev(dev); ps->dev = NULL; kfree(ps); @@ -546,7 +563,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) return -EINVAL; if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; - tmo = (ctrl.timeout * HZ + 999) / 1000; + tmo = ctrl.timeout; if (ctrl.bRequestType & 0x80) { if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.wLength)) { free_page((unsigned long)tbuf); @@ -555,8 +572,10 @@ static int proc_control(struct dev_state *ps, void __user *arg) snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); + usb_lock_device(dev); if ((i > 0) && ctrl.wLength) { if (usbfs_snoop) { dev_info(&dev->dev, "control read: data "); @@ -584,11 +603,13 @@ static int proc_control(struct dev_state *ps, void __user *arg) printk ("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } + usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); + usb_lock_device(dev); } free_page((unsigned long)tbuf); - if (i<0) { + if (i<0 && i != -EPIPE) { dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL " "failed cmd %s rqt %u rq %u len %u ret %d\n", current->comm, ctrl.bRequestType, ctrl.bRequest, @@ -619,15 +640,19 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) return -EINVAL; len1 = bulk.len; + if (len1 > MAX_USBFS_BUFFER_SIZE) + return -EINVAL; if (!(tbuf = kmalloc(len1, GFP_KERNEL))) return -ENOMEM; - tmo = (bulk.timeout * HZ + 999) / 1000; + tmo = bulk.timeout; if (bulk.ep & 0x80) { if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { kfree(tbuf); return -EINVAL; } + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + usb_lock_device(dev); if (!i && len2) { if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); @@ -641,14 +666,13 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + usb_lock_device(dev); } kfree(tbuf); - if (i < 0) { - dev_warn(&dev->dev, "usbfs: USBDEVFS_BULK failed " - "ep 0x%x len %u ret %d\n", bulk.ep, bulk.len, i); + if (i < 0) return i; - } return len2; } @@ -722,7 +746,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return __usb_reset_device(ps->dev); + return usb_reset_device(ps->dev); } @@ -787,49 +811,52 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg) return status; } -static int proc_submiturb(struct dev_state *ps, void __user *arg) + +static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, + struct usbdevfs_iso_packet_desc __user *iso_frame_desc, + void __user *arg) { - struct usbdevfs_urb uurb; struct usbdevfs_iso_packet_desc *isopkt = NULL; - struct usb_endpoint_descriptor *ep_desc; + struct usb_host_endpoint *ep; struct async *as; struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; int ret, interval = 0, ifnum = -1; - if (copy_from_user(&uurb, arg, sizeof(uurb))) - return -EFAULT; - if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK| + if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK| URB_NO_FSBR|URB_ZERO_PACKET)) return -EINVAL; - if (!uurb.buffer) + if (!uurb->buffer) return -EINVAL; - if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX)) + if (uurb->signr != 0 && (uurb->signr < SIGRTMIN || uurb->signr > SIGRTMAX)) return -EINVAL; - if (!(uurb.type == USBDEVFS_URB_TYPE_CONTROL && (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { - if ((ifnum = findintfep(ps->dev, uurb.endpoint)) < 0) + if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { + if ((ifnum = findintfep(ps->dev, uurb->endpoint)) < 0) return ifnum; if ((ret = checkintf(ps, ifnum))) return ret; } - switch(uurb.type) { + if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) + ep = ps->dev->ep_in [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; + else + ep = ps->dev->ep_out [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; + if (!ep) + return -ENOENT; + switch(uurb->type) { case USBDEVFS_URB_TYPE_CONTROL: - if ((uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) != 0) { - if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint))) - return -ENOENT; - if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_CONTROL) - return -EINVAL; - } + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_CONTROL) + return -EINVAL; /* min 8 byte setup packet, max arbitrary */ - if (uurb.buffer_length < 8 || uurb.buffer_length > PAGE_SIZE) + if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE) return -EINVAL; if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) return -ENOMEM; - if (copy_from_user(dr, uurb.buffer, 8)) { + if (copy_from_user(dr, uurb->buffer, 8)) { kfree(dr); return -EFAULT; } - if (uurb.buffer_length < (le16_to_cpup(&dr->wLength) + 8)) { + if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) { kfree(dr); return -EINVAL; } @@ -837,39 +864,46 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) kfree(dr); return ret; } - uurb.endpoint = (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK); - uurb.number_of_packets = 0; - uurb.buffer_length = le16_to_cpup(&dr->wLength); - uurb.buffer += 8; - if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) { + uurb->endpoint = (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK); + uurb->number_of_packets = 0; + uurb->buffer_length = le16_to_cpup(&dr->wLength); + uurb->buffer += 8; + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) { kfree(dr); return -EFAULT; } break; case USBDEVFS_URB_TYPE_BULK: - uurb.number_of_packets = 0; - if (uurb.buffer_length > 16384) + switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_ISOC: return -EINVAL; - if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) + /* allow single-shot interrupt transfers, at bogus rates */ + } + uurb->number_of_packets = 0; + if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) + return -EINVAL; + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; break; case USBDEVFS_URB_TYPE_ISO: /* arbitrary limit */ - if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128) + if (uurb->number_of_packets < 1 || uurb->number_of_packets > 128) + return -EINVAL; + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_ISOC) return -EINVAL; - if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint))) - return -ENOENT; - interval = 1 << min (15, ep_desc->bInterval - 1); - isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets; + interval = 1 << min (15, ep->desc.bInterval - 1); + isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets; if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) return -ENOMEM; - if (copy_from_user(isopkt, &((struct usbdevfs_urb __user *)arg)->iso_frame_desc, isofrmlen)) { + if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { kfree(isopkt); return -EFAULT; } - for (totlen = u = 0; u < uurb.number_of_packets; u++) { + for (totlen = u = 0; u < uurb->number_of_packets; u++) { if (isopkt[u].length > 1023) { kfree(isopkt); return -EINVAL; @@ -880,69 +914,65 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) kfree(isopkt); return -EINVAL; } - uurb.buffer_length = totlen; + uurb->buffer_length = totlen; break; case USBDEVFS_URB_TYPE_INTERRUPT: - uurb.number_of_packets = 0; - if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint))) - return -ENOENT; + uurb->number_of_packets = 0; + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_INT) + return -EINVAL; if (ps->dev->speed == USB_SPEED_HIGH) - interval = 1 << min (15, ep_desc->bInterval - 1); + interval = 1 << min (15, ep->desc.bInterval - 1); else - interval = ep_desc->bInterval; - if (uurb.buffer_length > 16384) + interval = ep->desc.bInterval; + if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; break; default: return -EINVAL; } - if (!(as = alloc_async(uurb.number_of_packets))) { - if (isopkt) - kfree(isopkt); - if (dr) - kfree(dr); + if (!(as = alloc_async(uurb->number_of_packets))) { + kfree(isopkt); + kfree(dr); return -ENOMEM; } - if (!(as->urb->transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) { - if (isopkt) - kfree(isopkt); - if (dr) - kfree(dr); + if (!(as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL))) { + kfree(isopkt); + kfree(dr); free_async(as); return -ENOMEM; } as->urb->dev = ps->dev; - as->urb->pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN); - as->urb->transfer_flags = uurb.flags; - as->urb->transfer_buffer_length = uurb.buffer_length; + as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | (uurb->endpoint & USB_DIR_IN); + as->urb->transfer_flags = uurb->flags; + as->urb->transfer_buffer_length = uurb->buffer_length; as->urb->setup_packet = (unsigned char*)dr; - as->urb->start_frame = uurb.start_frame; - as->urb->number_of_packets = uurb.number_of_packets; + as->urb->start_frame = uurb->start_frame; + as->urb->number_of_packets = uurb->number_of_packets; as->urb->interval = interval; as->urb->context = as; as->urb->complete = async_completed; - for (totlen = u = 0; u < uurb.number_of_packets; u++) { + for (totlen = u = 0; u < uurb->number_of_packets; u++) { as->urb->iso_frame_desc[u].offset = totlen; as->urb->iso_frame_desc[u].length = isopkt[u].length; totlen += isopkt[u].length; } - if (isopkt) - kfree(isopkt); + kfree(isopkt); as->ps = ps; as->userurb = arg; - if (uurb.endpoint & USB_DIR_IN) - as->userbuffer = uurb.buffer; + if (uurb->endpoint & USB_DIR_IN) + as->userbuffer = uurb->buffer; else as->userbuffer = NULL; - as->signr = uurb.signr; + as->signr = uurb->signr; as->ifnum = ifnum; as->task = current; - if (!(uurb.endpoint & USB_DIR_IN)) { - if (copy_from_user(as->urb->transfer_buffer, uurb.buffer, as->urb->transfer_buffer_length)) { + if (!(uurb->endpoint & USB_DIR_IN)) { + if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { free_async(as); return -EFAULT; } @@ -957,6 +987,16 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) return 0; } +static int proc_submiturb(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_urb uurb; + + if (copy_from_user(&uurb, arg, sizeof(uurb))) + return -EFAULT; + + return proc_do_submiturb(ps, &uurb, (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), arg); +} + static int proc_unlinkurb(struct dev_state *ps, void __user *arg) { struct async *as; @@ -964,14 +1004,15 @@ static int proc_unlinkurb(struct dev_state *ps, void __user *arg) as = async_getpending(ps, arg); if (!as) return -EINVAL; - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); return 0; } -static int processcompl(struct async *as) +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; if (as->userbuffer) @@ -984,50 +1025,51 @@ static int processcompl(struct async *as) if (put_user(urb->error_count, &userurb->error_count)) return -EFAULT; - if (!(usb_pipeisoc(urb->pipe))) - return 0; - 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; + if (usb_pipeisoc(urb->pipe)) { + 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; + } } + + free_async(as); + + if (put_user(addr, (void __user * __user *)arg)) + return -EFAULT; return 0; } -static int proc_reapurb(struct dev_state *ps, void __user *arg) +static struct async* reap_as(struct dev_state *ps) { DECLARE_WAITQUEUE(wait, current); struct async *as = NULL; - void __user *addr; struct usb_device *dev = ps->dev; - int ret; add_wait_queue(&ps->wait, &wait); - while (connected(dev)) { + for (;;) { __set_current_state(TASK_INTERRUPTIBLE); if ((as = async_getcompleted(ps))) break; if (signal_pending(current)) break; - up(&dev->serialize); + usb_unlock_device(dev); schedule(); - down(&dev->serialize); + usb_lock_device(dev); } remove_wait_queue(&ps->wait, &wait); set_current_state(TASK_RUNNING); - if (as) { - ret = processcompl(as); - addr = as->userurb; - free_async(as); - if (ret) - return ret; - if (put_user(addr, (void __user * __user *)arg)) - return -EFAULT; - return 0; - } + return as; +} + +static int proc_reapurb(struct dev_state *ps, void __user *arg) +{ + struct async *as = reap_as(ps); + if (as) + return processcompl(as, (void __user * __user *)arg); if (signal_pending(current)) return -EINTR; return -EIO; @@ -1036,21 +1078,105 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg) { struct async *as; - void __user *addr; - int ret; if (!(as = async_getcompleted(ps))) return -EAGAIN; - ret = processcompl(as); - addr = as->userurb; + return processcompl(as, (void __user * __user *)arg); +} + +#ifdef CONFIG_COMPAT + +static int get_urb32(struct usbdevfs_urb *kurb, + struct usbdevfs_urb32 __user *uurb) +{ + __u32 uptr; + if (get_user(kurb->type, &uurb->type) || + __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); + if (__get_user(uptr, &uurb->buffer)) + return -EFAULT; + kurb->usercontext = compat_ptr(uptr); + + return 0; +} + +static int proc_submiturb_compat(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_urb uurb; + + if (get_urb32(&uurb,(struct usbdevfs_urb32 *)arg)) + return -EFAULT; + + return proc_do_submiturb(ps, &uurb, ((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc, arg); +} + +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; + + if (as->userbuffer) + if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length)) + return -EFAULT; + if (put_user(urb->status, &userurb->status)) + return -EFAULT; + if (put_user(urb->actual_length, &userurb->actual_length)) + return -EFAULT; + if (put_user(urb->error_count, &userurb->error_count)) + return -EFAULT; + + if (usb_pipeisoc(urb->pipe)) { + 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; + } + } + free_async(as); - if (ret) - return ret; - if (put_user(addr, (void __user * __user *)arg)) + if (put_user((u32)(u64)addr, (u32 __user *)arg)) return -EFAULT; return 0; } +static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) +{ + struct async *as = reap_as(ps); + if (as) + return processcompl_compat(as, (void __user * __user *)arg); + if (signal_pending(current)) + return -EINTR; + return -EIO; +} + +static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg) +{ + struct async *as; + + if (!(as = async_getcompleted(ps))) + return -EAGAIN; + return processcompl_compat(as, (void __user * __user *)arg); +} + +#endif + static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) { struct usbdevfs_disconnectsignal ds; @@ -1094,16 +1220,17 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) int retval = 0; struct usb_interface *intf = NULL; struct usb_driver *driver = NULL; + int i; /* get input parameters and alloc buffer */ if (copy_from_user(&ctrl, arg, sizeof (ctrl))) return -EFAULT; if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) { - if ((buf = kmalloc (size, GFP_KERNEL)) == 0) + if ((buf = kmalloc (size, GFP_KERNEL)) == NULL) return -ENOMEM; if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) { if (copy_from_user (buf, ctrl.data, size)) { - kfree (buf); + kfree(buf); return -EFAULT; } } else { @@ -1112,19 +1239,28 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) } if (!connected(ps->dev)) { - if (buf) - kfree(buf); + kfree(buf); return -ENODEV; } if (ps->dev->state != USB_STATE_CONFIGURED) - retval = -ENODEV; + retval = -EHOSTUNREACH; else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno))) retval = -EINVAL; else switch (ctrl.ioctl_code) { /* disconnect kernel driver from interface */ case USBDEVFS_DISCONNECT: + + /* don't allow the user to unbind the hub driver from + * a hub with children to manage */ + for (i = 0; i < ps->dev->maxchild; ++i) { + if (ps->dev->children[i]) + retval = -EBUSY; + } + if (retval) + break; + down_write(&usb_bus_type.subsys.rwsem); if (intf->dev.driver) { driver = to_usb_driver(intf->dev.driver); @@ -1137,7 +1273,11 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: + usb_unlock_device(ps->dev); + usb_lock_all_devices(); bus_rescan_devices(intf->dev.bus); + usb_unlock_all_devices(); + usb_lock_device(ps->dev); break; /* talk directly to the interface's driver */ @@ -1145,7 +1285,7 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) down_read(&usb_bus_type.subsys.rwsem); if (intf->dev.driver) driver = to_usb_driver(intf->dev.driver); - if (driver == 0 || driver->ioctl == 0) { + if (driver == NULL || driver->ioctl == NULL) { retval = -ENOTTY; } else { retval = driver->ioctl (intf, ctrl.ioctl_code, buf); @@ -1161,8 +1301,8 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) && size > 0 && copy_to_user (ctrl.data, buf, size) != 0) retval = -EFAULT; - if (buf != 0) - kfree (buf); + + kfree(buf); return retval; } @@ -1180,9 +1320,9 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (!(file->f_mode & FMODE_WRITE)) return -EPERM; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { - up(&dev->serialize); + usb_unlock_device(dev); return -ENODEV; } @@ -1247,6 +1387,27 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd inode->i_mtime = CURRENT_TIME; break; +#ifdef CONFIG_COMPAT + + case USBDEVFS_SUBMITURB32: + snoop(&dev->dev, "%s: SUBMITURB32\n", __FUNCTION__); + ret = proc_submiturb_compat(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_REAPURB32: + snoop(&dev->dev, "%s: REAPURB32\n", __FUNCTION__); + ret = proc_reapurb_compat(ps, p); + break; + + case USBDEVFS_REAPURBNDELAY32: + snoop(&dev->dev, "%s: REAPURBDELAY32\n", __FUNCTION__); + ret = proc_reapurbnonblock_compat(ps, p); + break; + +#endif + case USBDEVFS_DISCARDURB: snoop(&dev->dev, "%s: DISCARDURB\n", __FUNCTION__); ret = proc_unlinkurb(ps, p); @@ -1282,7 +1443,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ret = proc_ioctl(ps, p); break; } - up(&dev->serialize); + usb_unlock_device(dev); if (ret >= 0) inode->i_atime = CURRENT_TIME; return ret; @@ -1302,7 +1463,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai return mask; } -struct file_operations usbdevfs_device_file_operations = { +struct file_operations usbfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll,