diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-17 14:24:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-17 14:24:26 -0700 |
commit | 48d10bda1f2c69980601a61194015bb0790fb7ab (patch) | |
tree | e4ea2021560b1f18b335f6e8e20761fb9514cd1b /drivers/usb/core | |
parent | 96b9b1c95660d4bc5510c5d798d3817ae9f0b391 (diff) | |
parent | ce53bfc4374cada8b645765e2b4ad5831e760932 (diff) |
Merge tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH:
"Here is the big USB patchset for 4.6-rc1.
The normal mess is here, gadget and xhci fixes and updates, and lots
of other driver updates and cleanups as well. Full details are in the
shortlog.
All have been in linux-next for a while with no reported issues"
* tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (266 commits)
USB: core: let USB device know device node
usb: devio: Add ioctl to disallow detaching kernel USB drivers.
usb: gadget: f_acm: Fix configfs attr name
usb: udc: lpc32xx: remove USB PLL and USB OTG clock management
usb: udc: lpc32xx: remove direct access to clock controller registers
usb: udc: lpc32xx: switch to clock prepare/unprepare model
usb: renesas_usbhs: gadget: fix giveback status code in usbhsg_pipe_disable()
usb: gadget: renesas_usb3: Use ARCH_RENESAS
usb: dwc2: Fix issues in dwc2_complete_non_isoc_xfer_ddma()
usb: dwc2: Add support for Lantiq ARX and XRX SoCs
usb: phy: generic: Handle late registration of gadget
usb: gadget: bdc_udc: fix race condition in bdc_udc_exit()
usb: musb: core: added missing const qualifier to musb_hdrc_platform_data::config
usb: dwc2: Move host-specific core functions into hcd.c
usb: dwc2: Move register save and restore functions
usb: dwc2: Use kmem_cache_free()
usb: dwc2: host: If using uframe scheduler, end splits better
usb: dwc2: host: Totally redo the microframe scheduler
usb: dwc2: host: Properly set even/odd frame
usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call
...
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/core/buffer.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/config.c | 37 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 26 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 301 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 114 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 122 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 7 | ||||
-rw-r--r-- | drivers/usb/core/of.c | 47 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 68 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 15 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 2 |
15 files changed, 608 insertions, 165 deletions
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 2f6f93220046..9780877010b4 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -5,7 +5,7 @@ usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o -usbcore-y += port.o +usbcore-y += port.o of.o usbcore-$(CONFIG_PCI) += hcd-pci.o usbcore-$(CONFIG_ACPI) += usb-acpi.o diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 89f2e7765093..2741566ee4f2 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -62,8 +62,9 @@ int hcd_buffer_create(struct usb_hcd *hcd) char name[16]; int i, size; - if (!hcd->self.controller->dma_mask && - !(hcd->driver->flags & HCD_LOCAL_MEM)) + if (!IS_ENABLED(CONFIG_HAS_DMA) || + (!hcd->self.controller->dma_mask && + !(hcd->driver->flags & HCD_LOCAL_MEM))) return 0; for (i = 0; i < HCD_BUFFER_POOLS; i++) { @@ -93,6 +94,9 @@ void hcd_buffer_destroy(struct usb_hcd *hcd) { int i; + if (!IS_ENABLED(CONFIG_HAS_DMA)) + return; + for (i = 0; i < HCD_BUFFER_POOLS; i++) { struct dma_pool *pool = hcd->pool[i]; @@ -119,8 +123,9 @@ void *hcd_buffer_alloc( int i; /* some USB hosts just use PIO */ - if (!bus->controller->dma_mask && - !(hcd->driver->flags & HCD_LOCAL_MEM)) { + if (!IS_ENABLED(CONFIG_HAS_DMA) || + (!bus->controller->dma_mask && + !(hcd->driver->flags & HCD_LOCAL_MEM))) { *dma = ~(dma_addr_t) 0; return kmalloc(size, mem_flags); } @@ -145,8 +150,9 @@ void hcd_buffer_free( if (!addr) return; - if (!bus->controller->dma_mask && - !(hcd->driver->flags & HCD_LOCAL_MEM)) { + if (!IS_ENABLED(CONFIG_HAS_DMA) || + (!bus->controller->dma_mask && + !(hcd->driver->flags & HCD_LOCAL_MEM))) { kfree(addr); return; } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 5050760f5e17..5eb1a87228b4 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -43,6 +43,27 @@ static int find_next_descriptor(unsigned char *buffer, int size, return buffer - buffer0; } +static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev, + int cfgno, int inum, int asnum, struct usb_host_endpoint *ep, + unsigned char *buffer, int size) +{ + struct usb_ssp_isoc_ep_comp_descriptor *desc; + + /* + * The SuperSpeedPlus Isoc endpoint companion descriptor immediately + * follows the SuperSpeed Endpoint Companion descriptor + */ + desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer; + if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP || + size < USB_DT_SSP_ISOC_EP_COMP_SIZE) { + dev_warn(ddev, "Invalid SuperSpeedPlus isoc endpoint companion" + "for config %d interface %d altsetting %d ep %d.\n", + cfgno, inum, asnum, ep->desc.bEndpointAddress); + return; + } + memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE); +} + static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_endpoint *ep, unsigned char *buffer, int size) @@ -54,6 +75,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, * be the first thing immediately following the endpoint descriptor. */ desc = (struct usb_ss_ep_comp_descriptor *) buffer; + buffer += desc->bLength; + size -= desc->bLength; + if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP || size < USB_DT_SS_EP_COMP_SIZE) { dev_warn(ddev, "No SuperSpeed endpoint companion for config %d " @@ -112,6 +136,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 16; } else if (usb_endpoint_xfer_isoc(&ep->desc) && + !USB_SS_SSP_ISOC_COMP(desc->bmAttributes) && USB_SS_MULT(desc->bmAttributes) > 3) { dev_warn(ddev, "Isoc endpoint has Mult of %d in " "config %d interface %d altsetting %d ep %d: " @@ -121,6 +146,12 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, ep->ss_ep_comp.bmAttributes = 2; } + /* Parse a possible SuperSpeedPlus isoc ep companion descriptor */ + if (usb_endpoint_xfer_isoc(&ep->desc) && + USB_SS_SSP_ISOC_COMP(desc->bmAttributes)) + usb_parse_ssp_isoc_endpoint_companion(ddev, cfgno, inum, asnum, + ep, buffer, size); + if (usb_endpoint_xfer_isoc(&ep->desc)) max_tx = (desc->bMaxBurst + 1) * (USB_SS_MULT(desc->bmAttributes)) * @@ -191,6 +222,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (usb_endpoint_xfer_int(d)) { i = 1; switch (to_usb_device(ddev)->speed) { + case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: case USB_SPEED_HIGH: /* Many device manufacturers are using full-speed @@ -274,7 +306,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, } /* Parse a possible SuperSpeed endpoint companion descriptor */ - if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) + if (to_usb_device(ddev)->speed >= USB_SPEED_SUPER) usb_parse_ss_endpoint_companion(ddev, cfgno, inum, asnum, endpoint, buffer, size); @@ -862,6 +894,9 @@ int usb_get_bos_descriptor(struct usb_device *dev) dev->bos->ss_id = (struct usb_ss_container_id_descriptor *)buffer; break; + case USB_PTM_CAP_TYPE: + dev->bos->ptm_cap = + (struct usb_ptm_cap_descriptor *)buffer; default: break; } diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index cffa0a0d7de2..ef04b50e6bbb 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -110,13 +110,6 @@ static const char format_endpt[] = /* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n"; - -/* - * Need access to the driver and USB bus lists. - * extern struct list_head usb_bus_list; - * However, these will come from functions that return ptrs to each of them. - */ - /* * Wait for an connect/disconnect event to happen. We initialize * the event counter with an odd number, and each event will increment @@ -221,7 +214,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, break; case USB_ENDPOINT_XFER_INT: type = "Int."; - if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER) + if (speed == USB_SPEED_HIGH || speed >= USB_SPEED_SUPER) interval = 1 << (desc->bInterval - 1); else interval = desc->bInterval; @@ -230,7 +223,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, return start; } interval *= (speed == USB_SPEED_HIGH || - speed == USB_SPEED_SUPER) ? 125 : 1000; + speed >= USB_SPEED_SUPER) ? 125 : 1000; if (interval % 1000) unit = 'u'; else { @@ -322,7 +315,7 @@ static char *usb_dump_config_descriptor(char *start, char *end, if (start > end) return start; - if (speed == USB_SPEED_SUPER) + if (speed >= USB_SPEED_SUPER) mul = 8; else mul = 2; @@ -534,6 +527,8 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, speed = "480"; break; case USB_SPEED_SUPER: speed = "5000"; break; + case USB_SPEED_SUPER_PLUS: + speed = "10000"; break; default: speed = "??"; } @@ -553,7 +548,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, /* super/high speed reserves 80%, full/low reserves 90% */ if (usbdev->speed == USB_SPEED_HIGH || - usbdev->speed == USB_SPEED_SUPER) + usbdev->speed >= USB_SPEED_SUPER) max = 800; else max = FRAME_TIME_MAX_USECS_ALLOC; @@ -616,6 +611,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, struct usb_bus *bus; ssize_t ret, total_written = 0; loff_t skip_bytes = *ppos; + int id; if (*ppos < 0) return -EINVAL; @@ -624,9 +620,9 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, if (!access_ok(VERIFY_WRITE, buf, nbytes)) return -EFAULT; - mutex_lock(&usb_bus_list_lock); + mutex_lock(&usb_bus_idr_lock); /* print devices for all busses */ - list_for_each_entry(bus, &usb_bus_list, bus_list) { + idr_for_each_entry(&usb_bus_idr, bus, id) { /* recurse through all children of the root hub */ if (!bus_to_hcd(bus)->rh_registered) continue; @@ -635,12 +631,12 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, bus->root_hub, bus, 0, 0, 0); usb_unlock_device(bus->root_hub); if (ret < 0) { - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); return ret; } total_written += ret; } - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); return total_written; } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 59e7a3369084..52c4461dfccd 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -50,6 +50,7 @@ #include <linux/user_namespace.h> #include <linux/scatterlist.h> #include <linux/uaccess.h> +#include <linux/dma-mapping.h> #include <asm/byteorder.h> #include <linux/moduleparam.h> @@ -69,6 +70,7 @@ struct usb_dev_state { spinlock_t lock; /* protects the async urb lists */ struct list_head async_pending; struct list_head async_completed; + struct list_head memory_list; wait_queue_head_t wait; /* wake up if a request completed */ unsigned int discsignr; struct pid *disc_pid; @@ -77,6 +79,19 @@ struct usb_dev_state { unsigned long ifclaimed; u32 secid; u32 disabled_bulk_eps; + bool privileges_dropped; + unsigned long interface_allowed_mask; +}; + +struct usb_memory { + struct list_head memlist; + int vma_use_count; + int urb_use_count; + u32 size; + void *mem; + dma_addr_t dma_handle; + unsigned long vm_start; + struct usb_dev_state *ps; }; struct async { @@ -89,6 +104,7 @@ struct async { void __user *userbuffer; void __user *userurb; struct urb *urb; + struct usb_memory *usbm; unsigned int mem_usage; int status; u32 secid; @@ -162,6 +178,111 @@ static int connected(struct usb_dev_state *ps) ps->dev->state != USB_STATE_NOTATTACHED); } +static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count) +{ + struct usb_dev_state *ps = usbm->ps; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + --*count; + if (usbm->urb_use_count == 0 && usbm->vma_use_count == 0) { + list_del(&usbm->memlist); + spin_unlock_irqrestore(&ps->lock, flags); + + usb_free_coherent(ps->dev, usbm->size, usbm->mem, + usbm->dma_handle); + usbfs_decrease_memory_usage( + usbm->size + sizeof(struct usb_memory)); + kfree(usbm); + } else { + spin_unlock_irqrestore(&ps->lock, flags); + } +} + +static void usbdev_vm_open(struct vm_area_struct *vma) +{ + struct usb_memory *usbm = vma->vm_private_data; + unsigned long flags; + + spin_lock_irqsave(&usbm->ps->lock, flags); + ++usbm->vma_use_count; + spin_unlock_irqrestore(&usbm->ps->lock, flags); +} + +static void usbdev_vm_close(struct vm_area_struct *vma) +{ + struct usb_memory *usbm = vma->vm_private_data; + + dec_usb_memory_use_count(usbm, &usbm->vma_use_count); +} + +struct vm_operations_struct usbdev_vm_ops = { + .open = usbdev_vm_open, + .close = usbdev_vm_close +}; + +static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct usb_memory *usbm = NULL; + struct usb_dev_state *ps = file->private_data; + size_t size = vma->vm_end - vma->vm_start; + void *mem; + unsigned long flags; + dma_addr_t dma_handle; + int ret; + + ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory)); + if (ret) + goto error; + + usbm = kzalloc(sizeof(struct usb_memory), GFP_KERNEL); + if (!usbm) { + ret = -ENOMEM; + goto error_decrease_mem; + } + + mem = usb_alloc_coherent(ps->dev, size, GFP_USER, &dma_handle); + if (!mem) { + ret = -ENOMEM; + goto error_free_usbm; + } + + memset(mem, 0, size); + + usbm->mem = mem; + usbm->dma_handle = dma_handle; + usbm->size = size; + usbm->ps = ps; + usbm->vm_start = vma->vm_start; + usbm->vma_use_count = 1; + INIT_LIST_HEAD(&usbm->memlist); + + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(usbm->mem) >> PAGE_SHIFT, + size, vma->vm_page_prot) < 0) { + dec_usb_memory_use_count(usbm, &usbm->vma_use_count); + return -EAGAIN; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP); + vma->vm_ops = &usbdev_vm_ops; + vma->vm_private_data = usbm; + + spin_lock_irqsave(&ps->lock, flags); + list_add_tail(&usbm->memlist, &ps->memory_list); + spin_unlock_irqrestore(&ps->lock, flags); + + return 0; + +error_free_usbm: + kfree(usbm); +error_decrease_mem: + usbfs_decrease_memory_usage(size + sizeof(struct usb_memory)); +error: + return ret; +} + static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { @@ -278,8 +399,13 @@ static void free_async(struct async *as) if (sg_page(&as->urb->sg[i])) kfree(sg_virt(&as->urb->sg[i])); } + kfree(as->urb->sg); - kfree(as->urb->transfer_buffer); + if (as->usbm == NULL) + kfree(as->urb->transfer_buffer); + else + dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count); + kfree(as->urb->setup_packet); usb_free_urb(as->urb); usbfs_decrease_memory_usage(as->mem_usage); @@ -624,6 +750,10 @@ static int claimintf(struct usb_dev_state *ps, unsigned int ifnum) if (test_bit(ifnum, &ps->ifclaimed)) return 0; + if (ps->privileges_dropped && + !test_bit(ifnum, &ps->interface_allowed_mask)) + return -EACCES; + intf = usb_ifnum_to_if(dev, ifnum); if (!intf) err = -ENOENT; @@ -848,7 +978,7 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt) (void *) (unsigned long) devt, match_devt); if (!dev) return NULL; - return container_of(dev, struct usb_device, dev); + return to_usb_device(dev); } /* @@ -861,7 +991,7 @@ static int usbdev_open(struct inode *inode, struct file *file) int ret; ret = -ENOMEM; - ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL); + ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL); if (!ps) goto out_free_ps; @@ -889,16 +1019,15 @@ static int usbdev_open(struct inode *inode, struct file *file) ps->dev = dev; ps->file = file; + ps->interface_allowed_mask = 0xFFFFFFFF; /* 32 bits */ spin_lock_init(&ps->lock); INIT_LIST_HEAD(&ps->list); INIT_LIST_HEAD(&ps->async_pending); INIT_LIST_HEAD(&ps->async_completed); + INIT_LIST_HEAD(&ps->memory_list); init_waitqueue_head(&ps->wait); - ps->discsignr = 0; ps->disc_pid = get_pid(task_pid(current)); ps->cred = get_current_cred(); - ps->disccontext = NULL; - ps->ifclaimed = 0; security_task_getsecid(current, &ps->secid); smp_wmb(); list_add_tail(&ps->list, &dev->filelist); @@ -945,6 +1074,7 @@ static int usbdev_release(struct inode *inode, struct file *file) free_async(as); as = async_getcompleted(ps); } + kfree(ps); return 0; } @@ -1198,6 +1328,28 @@ static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg) static int proc_resetdevice(struct usb_dev_state *ps) { + struct usb_host_config *actconfig = ps->dev->actconfig; + struct usb_interface *interface; + int i, number; + + /* Don't allow a device reset if the process has dropped the + * privilege to do such things and any of the interfaces are + * currently claimed. + */ + if (ps->privileges_dropped && actconfig) { + for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) { + interface = actconfig->interface[i]; + number = interface->cur_altsetting->desc.bInterfaceNumber; + if (usb_interface_claimed(interface) && + !test_bit(number, &ps->ifclaimed)) { + dev_warn(&ps->dev->dev, + "usbfs: interface %d claimed by %s while '%s' resets device\n", + number, interface->dev.driver->name, current->comm); + return -EACCES; + } + } + } + return usb_reset_device(ps->dev); } @@ -1266,6 +1418,31 @@ static int proc_setconfig(struct usb_dev_state *ps, void __user *arg) return status; } +static struct usb_memory * +find_memory_area(struct usb_dev_state *ps, const struct usbdevfs_urb *uurb) +{ + struct usb_memory *usbm = NULL, *iter; + unsigned long flags; + unsigned long uurb_start = (unsigned long)uurb->buffer; + + spin_lock_irqsave(&ps->lock, flags); + list_for_each_entry(iter, &ps->memory_list, memlist) { + if (uurb_start >= iter->vm_start && + uurb_start < iter->vm_start + iter->size) { + if (uurb->buffer_length > iter->vm_start + iter->size - + uurb_start) { + usbm = ERR_PTR(-EINVAL); + } else { + usbm = iter; + usbm->urb_use_count++; + } + break; + } + } + spin_unlock_irqrestore(&ps->lock, flags); + return usbm; +} + 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) @@ -1378,11 +1555,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb number_of_packets = uurb->number_of_packets; isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * number_of_packets; - isopkt = kmalloc(isofrmlen, GFP_KERNEL); - if (!isopkt) - return -ENOMEM; - if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { - ret = -EFAULT; + isopkt = memdup_user(iso_frame_desc, isofrmlen); + if (IS_ERR(isopkt)) { + ret = PTR_ERR(isopkt); + isopkt = NULL; goto error; } for (totlen = u = 0; u < number_of_packets; u++) { @@ -1422,6 +1598,19 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb goto error; } + as->usbm = find_memory_area(ps, uurb); + if (IS_ERR(as->usbm)) { + ret = PTR_ERR(as->usbm); + as->usbm = NULL; + goto error; + } + + /* do not use SG buffers when memory mapped segments + * are in use + */ + if (as->usbm) + num_sgs = 0; + u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length + num_sgs * sizeof(struct scatterlist); ret = usbfs_increase_memory_usage(u); @@ -1459,29 +1648,35 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb totlen -= u; } } else if (uurb->buffer_length > 0) { - as->urb->transfer_buffer = kmalloc(uurb->buffer_length, - GFP_KERNEL); - if (!as->urb->transfer_buffer) { - ret = -ENOMEM; - goto error; - } + if (as->usbm) { + unsigned long uurb_start = (unsigned long)uurb->buffer; - if (!is_in) { - if (copy_from_user(as->urb->transfer_buffer, - uurb->buffer, - uurb->buffer_length)) { - ret = -EFAULT; + as->urb->transfer_buffer = as->usbm->mem + + (uurb_start - as->usbm->vm_start); + } else { + as->urb->transfer_buffer = kmalloc(uurb->buffer_length, + GFP_KERNEL); + if (!as->urb->transfer_buffer) { + ret = -ENOMEM; goto error; } - } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { - /* - * Isochronous input data may end up being - * discontiguous if some of the packets are short. - * Clear the buffer so that the gaps don't leak - * kernel data to userspace. - */ - memset(as->urb->transfer_buffer, 0, - uurb->buffer_length); + if (!is_in) { + if (copy_from_user(as->urb->transfer_buffer, + uurb->buffer, + uurb->buffer_length)) { + ret = -EFAULT; + goto error; + } + } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { + /* + * Isochronous input data may end up being + * discontiguous if some of the packets are + * short. Clear the buffer so that the gaps + * don't leak kernel data to userspace. + */ + memset(as->urb->transfer_buffer, 0, + uurb->buffer_length); + } } } as->urb->dev = ps->dev; @@ -1528,10 +1723,14 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb isopkt = NULL; as->ps = ps; as->userurb = arg; - if (is_in && uurb->buffer_length > 0) + if (as->usbm) { + unsigned long uurb_start = (unsigned long)uurb->buffer; + + as->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + as->urb->transfer_dma = as->usbm->dma_handle + + (uurb_start - as->usbm->vm_start); + } else if (is_in && uurb->buffer_length > 0) as->userbuffer = uurb->buffer; - else - as->userbuffer = NULL; as->signr = uurb->signr; as->ifnum = ifnum; as->pid = get_pid(task_pid(current)); @@ -1587,6 +1786,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb return 0; error: + if (as && as->usbm) + dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count); kfree(isopkt); kfree(dr); if (as) @@ -1903,7 +2104,7 @@ static int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg) ret = releaseintf(ps, ifnum); if (ret < 0) return ret; - destroy_async_on_interface (ps, ifnum); + destroy_async_on_interface(ps, ifnum); return 0; } @@ -1915,6 +2116,9 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) struct usb_interface *intf = NULL; struct usb_driver *driver = NULL; + if (ps->privileges_dropped) + return -EACCES; + /* alloc buffer */ size = _IOC_SIZE(ctl->ioctl_code); if (size > 0) { @@ -2040,7 +2244,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg) __u32 caps; caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM | - USBDEVFS_CAP_REAP_AFTER_DISCONNECT; + USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP | + USBDEVFS_CAP_DROP_PRIVILEGES; if (!ps->dev->bus->no_stop_on_short) caps |= USBDEVFS_CAP_BULK_CONTINUATION; if (ps->dev->bus->sg_tablesize) @@ -2067,6 +2272,9 @@ static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg) if (intf->dev.driver) { struct usb_driver *driver = to_usb_driver(intf->dev.driver); + if (ps->privileges_dropped) + return -EACCES; + if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) && strncmp(dc.driver, intf->dev.driver->name, sizeof(dc.driver)) != 0) @@ -2123,6 +2331,23 @@ static int proc_free_streams(struct usb_dev_state *ps, void __user *arg) return r; } +static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg) +{ + u32 data; + + if (copy_from_user(&data, arg, sizeof(data))) + return -EFAULT; + + /* This is an one way operation. Once privileges are + * dropped, you cannot regain them. You may however reissue + * this ioctl to shrink the allowed interfaces mask. + */ + ps->interface_allowed_mask &= data; + ps->privileges_dropped = true; + + return 0; +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -2311,6 +2536,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, case USBDEVFS_FREE_STREAMS: ret = proc_free_streams(ps, p); break; + case USBDEVFS_DROP_PRIVILEGES: + ret = proc_drop_privileges(ps, p); + break; } done: @@ -2366,6 +2594,7 @@ const struct file_operations usbdev_file_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = usbdev_compat_ioctl, #endif + .mmap = usbdev_mmap, .open = usbdev_open, .release = usbdev_release, }; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index ea337a718cc1..822ced9639aa 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -19,6 +19,7 @@ #include <linux/errno.h> #include <linux/rwsem.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/usb.h> #include "usb.h" @@ -155,7 +156,6 @@ int usb_register_dev(struct usb_interface *intf, int minor_base = class_driver->minor_base; int minor; char name[20]; - char *temp; #ifdef CONFIG_USB_DYNAMIC_MINORS /* @@ -192,14 +192,9 @@ int usb_register_dev(struct usb_interface *intf, /* create a usb class device for this usb interface */ snprintf(name, sizeof(name), class_driver->name, minor - minor_base); - temp = strrchr(name, '/'); - if (temp && (temp[1] != '\0')) - ++temp; - else - temp = name; intf->usb_dev = device_create(usb_class->class, &intf->dev, MKDEV(USB_MAJOR, minor), class_driver, - "%s", temp); + "%s", kbasename(name)); if (IS_ERR(intf->usb_dev)) { down_write(&minor_rwsem); usb_minors[minor] = NULL; diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index c3640f8a8fb3..f9d42cf23e55 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -196,7 +196,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) * The xHCI driver has its own irq management * make sure irq setup is not touched for xhci in generic hcd code */ - if ((driver->flags & HCD_MASK) != HCD_USB3) { + if ((driver->flags & HCD_MASK) < HCD_USB3) { if (!dev->irq) { dev_err(&dev->dev, "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index df0e3b92533a..2ca2cef7f681 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -90,16 +90,15 @@ unsigned long usb_hcds_loaded; EXPORT_SYMBOL_GPL(usb_hcds_loaded); /* host controllers we manage */ -LIST_HEAD (usb_bus_list); -EXPORT_SYMBOL_GPL (usb_bus_list); +DEFINE_IDR (usb_bus_idr); +EXPORT_SYMBOL_GPL (usb_bus_idr); /* used when allocating bus numbers */ #define USB_MAXBUS 64 -static DECLARE_BITMAP(busmap, USB_MAXBUS); /* used when updating list of hcds */ -DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */ -EXPORT_SYMBOL_GPL (usb_bus_list_lock); +DEFINE_MUTEX(usb_bus_idr_lock); /* exported only for usbfs */ +EXPORT_SYMBOL_GPL (usb_bus_idr_lock); /* used for controlling access to virtual root hubs */ static DEFINE_SPINLOCK(hcd_root_hub_lock); @@ -128,6 +127,27 @@ static inline int is_root_hub(struct usb_device *udev) #define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff)) #define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff)) +/* usb 3.1 root hub device descriptor */ +static const u8 usb31_rh_dev_descriptor[18] = { + 0x12, /* __u8 bLength; */ + USB_DT_DEVICE, /* __u8 bDescriptorType; Device */ + 0x10, 0x03, /* __le16 bcdUSB; v3.1 */ + + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x03, /* __u8 bDeviceProtocol; USB 3 hub */ + 0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */ + + 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */ + 0x03, 0x00, /* __le16 idProduct; device 0x0003 */ + KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ + + 0x03, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + /* usb 3.0 root hub device descriptor */ static const u8 usb3_rh_dev_descriptor[18] = { 0x12, /* __u8 bLength; */ @@ -557,6 +577,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case USB_DT_DEVICE << 8: switch (hcd->speed) { case HCD_USB31: + bufp = usb31_rh_dev_descriptor; + break; case HCD_USB3: bufp = usb3_rh_dev_descriptor; break; @@ -645,9 +667,15 @@ nongeneric: /* non-generic request */ switch (typeReq) { case GetHubStatus: - case GetPortStatus: len = 4; break; + case GetPortStatus: + if (wValue == HUB_PORT_STATUS) + len = 4; + else + /* other port status types return 8 bytes */ + len = 8; + break; case GetHubDescriptor: len = sizeof (struct usb_hub_descriptor); break; @@ -967,8 +995,6 @@ static void usb_bus_init (struct usb_bus *bus) bus->bandwidth_int_reqs = 0; bus->bandwidth_isoc_reqs = 0; mutex_init(&bus->usb_address0_mutex); - - INIT_LIST_HEAD (&bus->bus_list); } /*-------------------------------------------------------------------------*/ @@ -988,18 +1014,14 @@ static int usb_register_bus(struct usb_bus *bus) int result = -E2BIG; int busnum; - mutex_lock(&usb_bus_list_lock); - busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1); - if (busnum >= USB_MAXBUS) { - printk (KERN_ERR "%s: too many buses\n", usbcore_name); + mutex_lock(&usb_bus_idr_lock); + busnum = idr_alloc(&usb_bus_idr, bus, 1, USB_MAXBUS, GFP_KERNEL); + if (busnum < 0) { + pr_err("%s: failed to get bus number\n", usbcore_name); goto error_find_busnum; } - set_bit(busnum, busmap); bus->busnum = busnum; - - /* Add it to the local list of buses */ - list_add (&bus->bus_list, &usb_bus_list); - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); usb_notify_add_bus(bus); @@ -1008,7 +1030,7 @@ static int usb_register_bus(struct usb_bus *bus) return 0; error_find_busnum: - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); return result; } @@ -1029,13 +1051,11 @@ static void usb_deregister_bus (struct usb_bus *bus) * controller code, as well as having it call this when cleaning * itself up */ - mutex_lock(&usb_bus_list_lock); - list_del (&bus->bus_list); - mutex_unlock(&usb_bus_list_lock); + mutex_lock(&usb_bus_idr_lock); + idr_remove(&usb_bus_idr, bus->busnum); + mutex_unlock(&usb_bus_idr_lock); usb_notify_remove_bus(bus); - - clear_bit(bus->busnum, busmap); } /** @@ -1063,12 +1083,12 @@ static int register_root_hub(struct usb_hcd *hcd) set_bit (devnum, usb_dev->bus->devmap.devicemap); usb_set_device_state(usb_dev, USB_STATE_ADDRESS); - mutex_lock(&usb_bus_list_lock); + mutex_lock(&usb_bus_idr_lock); usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); if (retval != sizeof usb_dev->descriptor) { - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); dev_dbg (parent_dev, "can't read %s device descriptor %d\n", dev_name(&usb_dev->dev), retval); return (retval < 0) ? retval : -EMSGSIZE; @@ -1078,8 +1098,8 @@ static int register_root_hub(struct usb_hcd *hcd) retval = usb_get_bos_descriptor(usb_dev); if (!retval) { usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev); - } else if (usb_dev->speed == USB_SPEED_SUPER) { - mutex_unlock(&usb_bus_list_lock); + } else if (usb_dev->speed >= USB_SPEED_SUPER) { + mutex_unlock(&usb_bus_idr_lock); dev_dbg(parent_dev, "can't read %s bos descriptor %d\n", dev_name(&usb_dev->dev), retval); return retval; @@ -1099,7 +1119,7 @@ static int register_root_hub(struct usb_hcd *hcd) if (HCD_DEAD(hcd)) usb_hc_died (hcd); /* This time clean up */ } - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); return retval; } @@ -1408,7 +1428,8 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) { - if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) + if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_SETUP_MAP_SINGLE)) dma_unmap_single(hcd->self.controller, urb->setup_dma, sizeof(struct usb_ctrlrequest), @@ -1440,17 +1461,20 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) usb_hcd_unmap_urb_setup_for_dma(hcd, urb); dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - if (urb->transfer_flags & URB_DMA_MAP_SG) + if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_DMA_MAP_SG)) dma_unmap_sg(hcd->self.controller, urb->sg, urb->num_sgs, dir); - else if (urb->transfer_flags & URB_DMA_MAP_PAGE) + else if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_DMA_MAP_PAGE)) dma_unmap_page(hcd->self.controller, urb->transfer_dma, urb->transfer_buffer_length, dir); - else if (urb->transfer_flags & URB_DMA_MAP_SINGLE) + else if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_DMA_MAP_SINGLE)) dma_unmap_single(hcd->self.controller, urb->transfer_dma, urb->transfer_buffer_length, @@ -1492,7 +1516,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (usb_endpoint_xfer_control(&urb->ep->desc)) { if (hcd->self.uses_pio_for_control) return ret; - if (hcd->self.uses_dma) { + if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) { urb->setup_dma = dma_map_single( hcd->self.controller, urb->setup_packet, @@ -1518,7 +1542,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_buffer_length != 0 && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { - if (hcd->self.uses_dma) { + if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) { if (urb->num_sgs) { int n; @@ -2112,7 +2136,7 @@ int usb_alloc_streams(struct usb_interface *interface, hcd = bus_to_hcd(dev->bus); if (!hcd->driver->alloc_streams || !hcd->driver->free_streams) return -EINVAL; - if (dev->speed != USB_SPEED_SUPER) + if (dev->speed < USB_SPEED_SUPER) return -EINVAL; if (dev->state < USB_STATE_CONFIGURED) return -ENODEV; @@ -2160,7 +2184,7 @@ int usb_free_streams(struct usb_interface *interface, dev = interface_to_usbdev(interface); hcd = bus_to_hcd(dev->bus); - if (dev->speed != USB_SPEED_SUPER) + if (dev->speed < USB_SPEED_SUPER) return -EINVAL; /* Double-free is not allowed */ @@ -2208,7 +2232,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev) int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) { - struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); + struct usb_hcd *hcd = bus_to_hcd(rhdev->bus); int status; int old_state = hcd->state; @@ -2257,7 +2281,7 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) { - struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); + struct usb_hcd *hcd = bus_to_hcd(rhdev->bus); int status; int old_state = hcd->state; @@ -2371,7 +2395,7 @@ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) * boards with root hubs hooked up to internal devices (instead of * just the OTG port) may need more attention to resetting... */ - hcd = container_of (bus, struct usb_hcd, self); + hcd = bus_to_hcd(bus); if (port_num && hcd->driver->start_port_reset) status = hcd->driver->start_port_reset(hcd, port_num); @@ -2778,9 +2802,11 @@ int usb_add_hcd(struct usb_hcd *hcd, rhdev->speed = USB_SPEED_WIRELESS; break; case HCD_USB3: - case HCD_USB31: rhdev->speed = USB_SPEED_SUPER; break; + case HCD_USB31: + rhdev->speed = USB_SPEED_SUPER_PLUS; + break; default: retval = -EINVAL; goto err_set_rh_speed; @@ -2863,9 +2889,9 @@ error_create_attr_group: #ifdef CONFIG_PM cancel_work_sync(&hcd->wakeup_work); #endif - mutex_lock(&usb_bus_list_lock); + mutex_lock(&usb_bus_idr_lock); usb_disconnect(&rhdev); /* Sets rhdev to NULL */ - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); err_register_root_hub: hcd->rh_pollable = 0; clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -2932,9 +2958,9 @@ void usb_remove_hcd(struct usb_hcd *hcd) cancel_work_sync(&hcd->wakeup_work); #endif - mutex_lock(&usb_bus_list_lock); + mutex_lock(&usb_bus_idr_lock); usb_disconnect(&rhdev); /* Sets rhdev to NULL */ - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); /* * tasklet_kill() isn't needed here because: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 51b436918f78..38cc4bae0a82 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -49,7 +49,7 @@ static void hub_event(struct work_struct *work); DEFINE_MUTEX(usb_port_peer_mutex); /* cycle leds on hubs that aren't blinking for attention */ -static bool blinkenlights = 0; +static bool blinkenlights; module_param(blinkenlights, bool, S_IRUGO); MODULE_PARM_DESC(blinkenlights, "true to cycle leds on hubs"); @@ -78,7 +78,7 @@ MODULE_PARM_DESC(initial_descriptor_timeout, * otherwise the new scheme is used. If that fails and "use_both_schemes" * is set, then the driver will make another attempt, using the other scheme. */ -static bool old_scheme_first = 0; +static bool old_scheme_first; module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(old_scheme_first, "start with the old device initialization scheme"); @@ -298,7 +298,7 @@ static void usb_set_lpm_parameters(struct usb_device *udev) unsigned int hub_u1_del; unsigned int hub_u2_del; - if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER) + if (!udev->lpm_capable || udev->speed < USB_SPEED_SUPER) return; hub = usb_hub_to_struct_hub(udev->parent); @@ -537,29 +537,34 @@ static int get_hub_status(struct usb_device *hdev, /* * USB 2.0 spec Section 11.24.2.7 + * USB 3.1 takes into use the wValue and wLength fields, spec Section 10.16.2.6 */ static int get_port_status(struct usb_device *hdev, int port1, - struct usb_port_status *data) + void *data, u16 value, u16 length) { int i, status = -ETIMEDOUT; for (i = 0; i < USB_STS_RETRIES && (status == -ETIMEDOUT || status == -EPIPE); i++) { status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1, - data, sizeof(*data), USB_STS_TIMEOUT); + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value, + port1, data, length, USB_STS_TIMEOUT); } return status; } -static int hub_port_status(struct usb_hub *hub, int port1, - u16 *status, u16 *change) +static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, + u16 *status, u16 *change, u32 *ext_status) { int ret; + int len = 4; + + if (type != HUB_PORT_STATUS) + len = 8; mutex_lock(&hub->status_mutex); - ret = get_port_status(hub->hdev, port1, &hub->status->port); - if (ret < 4) { + ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len); + if (ret < len) { if (ret != -ENODEV) dev_err(hub->intfdev, "%s failed (err = %d)\n", __func__, ret); @@ -568,13 +573,22 @@ static int hub_port_status(struct usb_hub *hub, int port1, } else { *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); - + if (type != HUB_PORT_STATUS && ext_status) + *ext_status = le32_to_cpu( + hub->status->port.dwExtPortStatus); ret = 0; } mutex_unlock(&hub->status_mutex); return ret; } +static int hub_port_status(struct usb_hub *hub, int port1, + u16 *status, u16 *change) +{ + return hub_ext_port_status(hub, port1, HUB_PORT_STATUS, + status, change, NULL); +} + static void kick_hub_wq(struct usb_hub *hub) { struct usb_interface *intf; @@ -2131,7 +2145,7 @@ static void hub_disconnect_children(struct usb_device *udev) * Something got disconnected. Get rid of it and all of its children. * * If *pdev is a normal device then the parent hub must already be locked. - * If *pdev is a root hub then the caller must hold the usb_bus_list_lock, + * If *pdev is a root hub then the caller must hold the usb_bus_idr_lock, * which protects the set of root hubs as well as the list of buses. * * Only hub drivers (including virtual root hub drivers for host @@ -2429,7 +2443,7 @@ static void set_usb_port_removable(struct usb_device *udev) * enumerated. The device descriptor is available, but not descriptors * for any device configuration. The caller must have locked either * the parent hub (if udev is a normal device) or else the - * usb_bus_list_lock (if udev is a root hub). The parent's pointer to + * usb_bus_idr_lock (if udev is a root hub). The parent's pointer to * udev has already been installed, but udev is not yet visible through * sysfs or other filesystem code. * @@ -2612,6 +2626,32 @@ out_authorized: return result; } +/* + * Return 1 if port speed is SuperSpeedPlus, 0 otherwise + * check it from the link protocol field of the current speed ID attribute. + * current speed ID is got from ext port status request. Sublink speed attribute + * table is returned with the hub BOS SSP device capability descriptor + */ +static int port_speed_is_ssp(struct usb_device *hdev, int speed_id) +{ + int ssa_count; + u32 ss_attr; + int i; + struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap; + + if (!ssp_cap) + return 0; + + ssa_count = le32_to_cpu(ssp_cap->bmAttributes) & + USB_SSP_SUBLINK_SPEED_ATTRIBS; + + for (i = 0; i <= ssa_count; i++) { + ss_attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]); + if (speed_id == (ss_attr & USB_SSP_SUBLINK_SPEED_SSID)) + return !!(ss_attr & USB_SSP_SUBLINK_SPEED_LP); + } + return 0; +} /* Returns 1 if @hub is a WUSB root hub, 0 otherwise */ static unsigned hub_is_wusb(struct usb_hub *hub) @@ -2619,7 +2659,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub) struct usb_hcd *hcd; if (hub->hdev->parent != NULL) /* not a root hub? */ return 0; - hcd = container_of(hub->hdev->bus, struct usb_hcd, self); + hcd = bus_to_hcd(hub->hdev->bus); return hcd->wireless; } @@ -2645,7 +2685,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub) */ static bool use_new_scheme(struct usb_device *udev, int retry) { - if (udev->speed == USB_SPEED_SUPER) + if (udev->speed >= USB_SPEED_SUPER) return false; return USE_NEW_SCHEME(retry); @@ -2676,6 +2716,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, int delay_time, ret; u16 portstatus; u16 portchange; + u32 ext_portstatus = 0; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; @@ -2684,7 +2725,14 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, msleep(delay); /* read and decode port status */ - ret = hub_port_status(hub, port1, &portstatus, &portchange); + if (hub_is_superspeedplus(hub->hdev)) + ret = hub_ext_port_status(hub, port1, + HUB_EXT_PORT_STATUS, + &portstatus, &portchange, + &ext_portstatus); + else + ret = hub_port_status(hub, port1, &portstatus, + &portchange); if (ret < 0) return ret; @@ -2727,6 +2775,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (hub_is_wusb(hub)) udev->speed = USB_SPEED_WIRELESS; + else if (hub_is_superspeedplus(hub->hdev) && + port_speed_is_ssp(hub->hdev, ext_portstatus & + USB_EXT_PORT_STAT_RX_SPEED_ID)) + udev->speed = USB_SPEED_SUPER_PLUS; else if (hub_is_superspeed(hub->hdev)) udev->speed = USB_SPEED_SUPER; else if (portstatus & USB_PORT_STAT_HIGH_SPEED) @@ -3989,7 +4041,7 @@ int usb_disable_lpm(struct usb_device *udev) struct usb_hcd *hcd; if (!udev || !udev->parent || - udev->speed != USB_SPEED_SUPER || + udev->speed < USB_SPEED_SUPER || !udev->lpm_capable || udev->state < USB_STATE_DEFAULT) return 0; @@ -4048,7 +4100,7 @@ void usb_enable_lpm(struct usb_device *udev) struct usb_port *port_dev; if (!udev || !udev->parent || - udev->speed != USB_SPEED_SUPER || + udev->speed < USB_SPEED_SUPER || !udev->lpm_capable || udev->state < USB_STATE_DEFAULT) return; @@ -4292,7 +4344,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, { struct usb_device *hdev = hub->hdev; struct usb_hcd *hcd = bus_to_hcd(hdev->bus); - int i, j, retval; + int retries, operations, retval, i; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; const char *speed; @@ -4323,7 +4375,9 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, retval = -ENODEV; - if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { + /* Don't allow speed changes at reset, except usb 3.0 to faster */ + if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed && + !(oldspeed == USB_SPEED_SUPER && udev->speed > oldspeed)) { dev_dbg(&udev->dev, "device reset changed speed!\n"); goto fail; } @@ -4335,6 +4389,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. */ switch (udev->speed) { + case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: case USB_SPEED_WIRELESS: /* fixed at 512 */ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); @@ -4361,7 +4416,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, else speed = usb_speed_string(udev->speed); - if (udev->speed != USB_SPEED_SUPER) + if (udev->speed < USB_SPEED_SUPER) dev_info(&udev->dev, "%s %s USB device number %d using %s\n", (udev->config) ? "reset" : "new", speed, @@ -4394,7 +4449,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * first 8 bytes of the device descriptor to get the ep0 maxpacket * value. */ - for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { + for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) { bool did_new_scheme = false; if (use_new_scheme(udev, retry_counter)) { @@ -4421,7 +4476,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * 255 is for WUSB devices, we actually need to use * 512 (WUSB1.0[4.8.1]). */ - for (j = 0; j < 3; ++j) { + for (operations = 0; operations < 3; ++operations) { buf->bMaxPacketSize0 = 0; r = usb_control_msg(udev, usb_rcvaddr0pipe(), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, @@ -4441,7 +4496,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, r = -EPROTO; break; } - if (r == 0) + /* + * Some devices time out if they are powered on + * when already connected. They need a second + * reset. But only on the first attempt, + * lest we get into a time out/reset loop + */ + if (r == 0 || (r == -ETIMEDOUT && retries == 0)) break; } udev->descriptor.bMaxPacketSize0 = @@ -4473,7 +4534,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * authorization will assign the final address. */ if (udev->wusb == 0) { - for (j = 0; j < SET_ADDRESS_TRIES; ++j) { + for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) { retval = hub_set_address(udev, devnum); if (retval >= 0) break; @@ -4485,11 +4546,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, devnum, retval); goto fail; } - if (udev->speed == USB_SPEED_SUPER) { + if (udev->speed >= USB_SPEED_SUPER) { devnum = udev->devnum; dev_info(&udev->dev, - "%s SuperSpeed USB device number %d using %s\n", + "%s SuperSpeed%s USB device number %d using %s\n", (udev->config) ? "reset" : "new", + (udev->speed == USB_SPEED_SUPER_PLUS) ? "Plus" : "", devnum, udev->bus->controller->driver->name); } @@ -4528,7 +4590,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * got from those devices show they aren't superspeed devices. Warm * reset the port attached by the devices can fix them. */ - if ((udev->speed == USB_SPEED_SUPER) && + if ((udev->speed >= USB_SPEED_SUPER) && (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) { dev_err(&udev->dev, "got a wrong device descriptor, " "warm reset device\n"); @@ -4539,7 +4601,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, } if (udev->descriptor.bMaxPacketSize0 == 0xff || - udev->speed == USB_SPEED_SUPER) + udev->speed >= USB_SPEED_SUPER) i = 512; else i = udev->descriptor.bMaxPacketSize0; @@ -4749,7 +4811,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, udev->level = hdev->level + 1; udev->wusb = hub_is_wusb(hub); - /* Only USB 3.0 devices are connected to SuperSpeed hubs. */ + /* Devices connected to SuperSpeed hubs are USB 3.0 or later */ if (hub_is_superspeed(hub->hdev)) udev->speed = USB_SPEED_SUPER; else diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 45d070dd1d03..34c1a7e22aae 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -140,6 +140,13 @@ static inline int hub_is_superspeed(struct usb_device *hdev) return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS; } +static inline int hub_is_superspeedplus(struct usb_device *hdev) +{ + return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS && + le16_to_cpu(hdev->descriptor.bcdUSB) >= 0x0310 && + hdev->bos->ssp_cap); +} + static inline unsigned hub_power_on_good_delay(struct usb_hub *hub) { unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2; diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c new file mode 100644 index 000000000000..2289700c31d6 --- /dev/null +++ b/drivers/usb/core/of.c @@ -0,0 +1,47 @@ +/* + * of.c The helpers for hcd device tree support + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Author: Peter Chen <peter.chen@freescale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/of.h> + +/** + * usb_of_get_child_node - Find the device node match port number + * @parent: the parent device node + * @portnum: the port number which device is connecting + * + * Find the node from device tree according to its port number. + * + * Return: On success, a pointer to the device node, %NULL on failure. + */ +struct device_node *usb_of_get_child_node(struct device_node *parent, + int portnum) +{ + struct device_node *node; + u32 port; + + for_each_child_of_node(parent, node) { + if (!of_property_read_u32(node, "reg", &port)) { + if (port == portnum) + return node; + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(usb_of_get_child_node); + diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 65b6e6b84043..c953a0f1c695 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -23,10 +23,12 @@ static ssize_t field##_show(struct device *dev, \ { \ struct usb_device *udev; \ struct usb_host_config *actconfig; \ - ssize_t rc = 0; \ + ssize_t rc; \ \ udev = to_usb_device(dev); \ - usb_lock_device(udev); \ + rc = usb_lock_device_interruptible(udev); \ + if (rc < 0) \ + return -EINTR; \ actconfig = udev->actconfig; \ if (actconfig) \ rc = sprintf(buf, format_string, \ @@ -47,10 +49,12 @@ static ssize_t bMaxPower_show(struct device *dev, { struct usb_device *udev; struct usb_host_config *actconfig; - ssize_t rc = 0; + ssize_t rc; udev = to_usb_device(dev); - usb_lock_device(udev); + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; actconfig = udev->actconfig; if (actconfig) rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig)); @@ -64,10 +68,12 @@ static ssize_t configuration_show(struct device *dev, { struct usb_device *udev; struct usb_host_config *actconfig; - ssize_t rc = 0; + ssize_t rc; udev = to_usb_device(dev); - usb_lock_device(udev); + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; actconfig = udev->actconfig; if (actconfig && actconfig->string) rc = sprintf(buf, "%s\n", actconfig->string); @@ -84,11 +90,13 @@ static ssize_t bConfigurationValue_store(struct device *dev, const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); - int config, value; + int config, value, rc; if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255) return -EINVAL; - usb_lock_device(udev); + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; value = usb_set_configuration(udev, config); usb_unlock_device(udev); return (value < 0) ? value : count; @@ -105,7 +113,9 @@ static ssize_t name##_show(struct device *dev, \ int retval; \ \ udev = to_usb_device(dev); \ - usb_lock_device(udev); \ + retval = usb_lock_device_interruptible(udev); \ + if (retval < 0) \ + return -EINTR; \ retval = sprintf(buf, "%s\n", udev->name); \ usb_unlock_device(udev); \ return retval; \ @@ -141,6 +151,9 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, case USB_SPEED_SUPER: speed = "5000"; break; + case USB_SPEED_SUPER_PLUS: + speed = "10000"; + break; default: speed = "unknown"; } @@ -224,11 +237,13 @@ static ssize_t avoid_reset_quirk_store(struct device *dev, const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); - int val; + int val, rc; if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1) return -EINVAL; - usb_lock_device(udev); + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; if (val) udev->quirks |= USB_QUIRK_RESET; else @@ -294,7 +309,7 @@ static ssize_t persist_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); - int value; + int value, rc; /* Hubs are always enabled for USB_PERSIST */ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) @@ -303,7 +318,9 @@ static ssize_t persist_store(struct device *dev, struct device_attribute *attr, if (sscanf(buf, "%d", &value) != 1) return -EINVAL; - usb_lock_device(udev); + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; udev->persist_enabled = !!value; usb_unlock_device(udev); return count; @@ -420,13 +437,16 @@ static ssize_t level_store(struct device *dev, struct device_attribute *attr, int len = count; char *cp; int rc = count; + int rv; warn_level(); cp = memchr(buf, '\n', count); if (cp) len = cp - buf; - usb_lock_device(udev); + rv = usb_lock_device_interruptible(udev); + if (rv < 0) + return -EINTR; if (len == sizeof on_string - 1 && strncmp(buf, on_string, len) == 0) @@ -466,7 +486,9 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev, bool value; int ret; - usb_lock_device(udev); + ret = usb_lock_device_interruptible(udev); + if (ret < 0) + return -EINTR; ret = strtobool(buf, &value); @@ -536,8 +558,11 @@ static ssize_t usb3_hardware_lpm_u1_show(struct device *dev, { struct usb_device *udev = to_usb_device(dev); const char *p; + int rc; - usb_lock_device(udev); + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; if (udev->usb3_lpm_u1_enabled) p = "enabled"; @@ -555,8 +580,11 @@ static ssize_t usb3_hardware_lpm_u2_show(struct device *dev, { struct usb_device *udev = to_usb_device(dev); const char *p; + int rc; - usb_lock_device(udev); + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; if (udev->usb3_lpm_u2_enabled) p = "enabled"; @@ -822,7 +850,6 @@ read_descriptors(struct file *filp, struct kobject *kobj, * Following that are the raw descriptor entries for all the * configurations (config plus subsidiary descriptors). */ - usb_lock_device(udev); for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations && nleft > 0; ++cfgno) { if (cfgno < 0) { @@ -843,7 +870,6 @@ read_descriptors(struct file *filp, struct kobject *kobj, off -= srclen; } } - usb_unlock_device(udev); return count - nleft; } @@ -969,7 +995,9 @@ static ssize_t supports_autosuspend_show(struct device *dev, { int s; - device_lock(dev); + s = device_lock_interruptible(dev); + if (s < 0) + return -EINTR; /* Devices will be autosuspended even when an interface isn't claimed */ s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend); device_unlock(dev); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3d274778caaf..c601e25b609f 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -401,7 +401,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) /* SuperSpeed isoc endpoints have up to 16 bursts of up to * 3 packets each */ - if (dev->speed == USB_SPEED_SUPER) { + if (dev->speed >= USB_SPEED_SUPER) { int burst = 1 + ep->ss_ep_comp.bMaxBurst; int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes); max *= burst; @@ -499,6 +499,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) } /* too big? */ switch (dev->speed) { + case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: /* units are 125us */ /* Handle up to 2^(16-1) microframes */ if (urb->interval > (1 << 15)) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index ebb29caa3fe4..ffa5cf13ffe1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -36,6 +36,7 @@ #include <linux/mutex.h> #include <linux/workqueue.h> #include <linux/debugfs.h> +#include <linux/usb/of.h> #include <asm/io.h> #include <linux/scatterlist.h> @@ -241,7 +242,7 @@ static int __each_dev(struct device *dev, void *data) if (!is_usb_device(dev)) return 0; - return arg->fn(container_of(dev, struct usb_device, dev), arg->data); + return arg->fn(to_usb_device(dev), arg->data); } /** @@ -397,7 +398,7 @@ struct device_type usb_device_type = { /* Returns 1 if @usb_bus is WUSB, 0 otherwise */ static unsigned usb_bus_is_wusb(struct usb_bus *bus) { - struct usb_hcd *hcd = container_of(bus, struct usb_hcd, self); + struct usb_hcd *hcd = bus_to_hcd(bus); return hcd->wireless; } @@ -470,6 +471,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->route = 0; dev->dev.parent = bus->controller; + dev->dev.of_node = bus->controller->of_node; dev_set_name(&dev->dev, "usb%d", bus->busnum); root_hub = 1; } else { @@ -494,6 +496,14 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->dev.parent = &parent->dev; dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath); + if (!parent->parent) { + /* device under root hub's port */ + port1 = usb_hcd_find_raw_port_number(usb_hcd, + port1); + } + dev->dev.of_node = usb_of_get_child_node(parent->dev.of_node, + port1); + /* hub driver sets up TT records */ } @@ -1115,6 +1125,7 @@ static void __exit usb_exit(void) bus_unregister(&usb_bus_type); usb_acpi_unregister(); usb_debugfs_cleanup(); + idr_destroy(&usb_bus_idr); } subsys_initcall(usb_init); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 05b5e17abf92..53318126ed91 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -45,7 +45,7 @@ static inline unsigned usb_get_max_power(struct usb_device *udev, struct usb_host_config *c) { /* SuperSpeed power is in 8 mA units; others are in 2 mA units */ - unsigned mul = (udev->speed == USB_SPEED_SUPER ? 8 : 2); + unsigned mul = (udev->speed >= USB_SPEED_SUPER ? 8 : 2); return c->desc.bMaxPower * mul; } |