mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 08:48:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
86
drivers/usb/core/Kconfig
Normal file
86
drivers/usb/core/Kconfig
Normal file
|
@ -0,0 +1,86 @@
|
|||
#
|
||||
# USB Core configuration
|
||||
#
|
||||
config USB_ANNOUNCE_NEW_DEVICES
|
||||
bool "USB announce new devices"
|
||||
help
|
||||
Say Y here if you want the USB core to always announce the
|
||||
idVendor, idProduct, Manufacturer, Product, and SerialNumber
|
||||
strings for every new USB device to the syslog. This option is
|
||||
usually used by distro vendors to help with debugging and to
|
||||
let users know what specific device was added to the machine
|
||||
in what location.
|
||||
|
||||
If you do not want this kind of information sent to the system
|
||||
log, or have any doubts about this, say N here.
|
||||
|
||||
comment "Miscellaneous USB options"
|
||||
|
||||
config USB_DEFAULT_PERSIST
|
||||
bool "Enable USB persist by default"
|
||||
default y
|
||||
help
|
||||
Say N here if you don't want USB power session persistence
|
||||
enabled by default. If you say N it will make suspended USB
|
||||
devices that lose power get reenumerated as if they had been
|
||||
unplugged, causing any mounted filesystems to be lost. The
|
||||
persist feature can still be enabled for individual devices
|
||||
through the power/persist sysfs node. See
|
||||
Documentation/usb/persist.txt for more info.
|
||||
|
||||
If you have any questions about this, say Y here, only say N
|
||||
if you know exactly what you are doing.
|
||||
|
||||
config USB_DYNAMIC_MINORS
|
||||
bool "Dynamic USB minor allocation"
|
||||
help
|
||||
If you say Y here, the USB subsystem will use dynamic minor
|
||||
allocation for any device that uses the USB major number.
|
||||
This means that you can have more than 16 of a single type
|
||||
of device (like USB printers).
|
||||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
config USB_OTG
|
||||
bool "OTG support"
|
||||
depends on PM_RUNTIME
|
||||
default n
|
||||
help
|
||||
The most notable feature of USB OTG is support for a
|
||||
"Dual-Role" device, which can act as either a device
|
||||
or a host. The initial role is decided by the type of
|
||||
plug inserted and can be changed later when two dual
|
||||
role devices talk to each other.
|
||||
|
||||
Select this only if your board has Mini-AB/Micro-AB
|
||||
connector.
|
||||
|
||||
config USB_OTG_WHITELIST
|
||||
bool "Rely on OTG and EH Targeted Peripherals List"
|
||||
depends on USB
|
||||
help
|
||||
If you say Y here, the "otg_whitelist.h" file will be used as a
|
||||
product whitelist, so USB peripherals not listed there will be
|
||||
rejected during enumeration. This behavior is required by the
|
||||
USB OTG and EH specification for all devices not on your product's
|
||||
"Targeted Peripherals List". "Embedded Hosts" are likewise
|
||||
allowed to support only a limited number of peripherals.
|
||||
|
||||
config USB_OTG_BLACKLIST_HUB
|
||||
bool "Disable external hubs"
|
||||
depends on USB_OTG || EXPERT
|
||||
help
|
||||
If you say Y here, then Linux will refuse to enumerate
|
||||
external hubs. OTG hosts are allowed to reduce hardware
|
||||
and software costs by not supporting external hubs. So
|
||||
are "Embedded Hosts" that don't offer OTG support.
|
||||
|
||||
config USB_OTG_FSM
|
||||
tristate "USB 2.0 OTG FSM implementation"
|
||||
depends on USB
|
||||
select USB_OTG
|
||||
select USB_PHY
|
||||
help
|
||||
Implements OTG Finite State Machine as specified in On-The-Go
|
||||
and Embedded Host Supplement to the USB Revision 2.0 Specification.
|
||||
|
13
drivers/usb/core/Makefile
Normal file
13
drivers/usb/core/Makefile
Normal file
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Makefile for USB Core files and filesystem
|
||||
#
|
||||
|
||||
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-$(CONFIG_PCI) += hcd-pci.o
|
||||
usbcore-$(CONFIG_ACPI) += usb-acpi.o
|
||||
|
||||
obj-$(CONFIG_USB) += usbcore.o
|
160
drivers/usb/core/buffer.c
Normal file
160
drivers/usb/core/buffer.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* DMA memory management for framework level HCD code (hc_driver)
|
||||
*
|
||||
* This implementation plugs in through generic "usb_bus" level methods,
|
||||
* and should work with all USB controllers, regardless of bus type.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
|
||||
/*
|
||||
* DMA-Coherent Buffers
|
||||
*/
|
||||
|
||||
/* FIXME tune these based on pool statistics ... */
|
||||
static size_t pool_max[HCD_BUFFER_POOLS] = {
|
||||
32, 128, 512, 2048,
|
||||
};
|
||||
|
||||
void __init usb_init_pool_max(void)
|
||||
{
|
||||
/*
|
||||
* The pool_max values must never be smaller than
|
||||
* ARCH_KMALLOC_MINALIGN.
|
||||
*/
|
||||
if (ARCH_KMALLOC_MINALIGN <= 32)
|
||||
; /* Original value is okay */
|
||||
else if (ARCH_KMALLOC_MINALIGN <= 64)
|
||||
pool_max[0] = 64;
|
||||
else if (ARCH_KMALLOC_MINALIGN <= 128)
|
||||
pool_max[0] = 0; /* Don't use this pool */
|
||||
else
|
||||
BUILD_BUG(); /* We don't allow this */
|
||||
}
|
||||
|
||||
/* SETUP primitives */
|
||||
|
||||
/**
|
||||
* hcd_buffer_create - initialize buffer pools
|
||||
* @hcd: the bus whose buffer pools are to be initialized
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Call this as part of initializing a host controller that uses the dma
|
||||
* memory allocators. It initializes some pools of dma-coherent memory that
|
||||
* will be shared by all drivers using that controller.
|
||||
*
|
||||
* Call hcd_buffer_destroy() to clean up after using those pools.
|
||||
*
|
||||
* Return: 0 if successful. A negative errno value otherwise.
|
||||
*/
|
||||
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))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
size = pool_max[i];
|
||||
if (!size)
|
||||
continue;
|
||||
snprintf(name, sizeof name, "buffer-%d", size);
|
||||
hcd->pool[i] = dma_pool_create(name, hcd->self.controller,
|
||||
size, size, 0);
|
||||
if (!hcd->pool[i]) {
|
||||
hcd_buffer_destroy(hcd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hcd_buffer_destroy - deallocate buffer pools
|
||||
* @hcd: the bus whose buffer pools are to be destroyed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* This frees the buffer pools created by hcd_buffer_create().
|
||||
*/
|
||||
void hcd_buffer_destroy(struct usb_hcd *hcd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
struct dma_pool *pool = hcd->pool[i];
|
||||
if (pool) {
|
||||
dma_pool_destroy(pool);
|
||||
hcd->pool[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* sometimes alloc/free could use kmalloc with GFP_DMA, for
|
||||
* better sharing and to leverage mm/slab.c intelligence.
|
||||
*/
|
||||
|
||||
void *hcd_buffer_alloc(
|
||||
struct usb_bus *bus,
|
||||
size_t size,
|
||||
gfp_t mem_flags,
|
||||
dma_addr_t *dma
|
||||
)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(bus);
|
||||
int i;
|
||||
|
||||
/* some USB hosts just use PIO */
|
||||
if (!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)) {
|
||||
*dma = ~(dma_addr_t) 0;
|
||||
return kmalloc(size, mem_flags);
|
||||
}
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
if (size <= pool_max[i])
|
||||
return dma_pool_alloc(hcd->pool[i], mem_flags, dma);
|
||||
}
|
||||
return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags);
|
||||
}
|
||||
|
||||
void hcd_buffer_free(
|
||||
struct usb_bus *bus,
|
||||
size_t size,
|
||||
void *addr,
|
||||
dma_addr_t dma
|
||||
)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(bus);
|
||||
int i;
|
||||
|
||||
if (!addr)
|
||||
return;
|
||||
|
||||
if (!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)) {
|
||||
kfree(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
if (size <= pool_max[i]) {
|
||||
dma_pool_free(hcd->pool[i], addr, dma);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dma_free_coherent(hcd->self.controller, size, addr, dma);
|
||||
}
|
869
drivers/usb/core/config.c
Normal file
869
drivers/usb/core/config.c
Normal file
|
@ -0,0 +1,869 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "usb.h"
|
||||
|
||||
|
||||
#define USB_MAXALTSETTING 128 /* Hard limit */
|
||||
|
||||
#define USB_MAXCONFIG 8 /* Arbitrary limit */
|
||||
|
||||
|
||||
static inline const char *plural(int n)
|
||||
{
|
||||
return (n == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
static int find_next_descriptor(unsigned char *buffer, int size,
|
||||
int dt1, int dt2, int *num_skipped)
|
||||
{
|
||||
struct usb_descriptor_header *h;
|
||||
int n = 0;
|
||||
unsigned char *buffer0 = buffer;
|
||||
|
||||
/* Find the next descriptor of type dt1 or dt2 */
|
||||
while (size > 0) {
|
||||
h = (struct usb_descriptor_header *) buffer;
|
||||
if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
|
||||
break;
|
||||
buffer += h->bLength;
|
||||
size -= h->bLength;
|
||||
++n;
|
||||
}
|
||||
|
||||
/* Store the number of descriptors skipped and return the
|
||||
* number of bytes skipped */
|
||||
if (num_skipped)
|
||||
*num_skipped = n;
|
||||
return buffer - buffer0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct usb_ss_ep_comp_descriptor *desc;
|
||||
int max_tx;
|
||||
|
||||
/* The SuperSpeed endpoint companion descriptor is supposed to
|
||||
* be the first thing immediately following the endpoint descriptor.
|
||||
*/
|
||||
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
|
||||
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 "
|
||||
" interface %d altsetting %d ep %d: "
|
||||
"using minimum values\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
|
||||
/* Fill in some default values.
|
||||
* Leave bmAttributes as zero, which will mean no streams for
|
||||
* bulk, and isoc won't support multiple bursts of packets.
|
||||
* With bursts of only one packet, and a Mult of 1, the max
|
||||
* amount of data moved per endpoint service interval is one
|
||||
* packet.
|
||||
*/
|
||||
ep->ss_ep_comp.bLength = USB_DT_SS_EP_COMP_SIZE;
|
||||
ep->ss_ep_comp.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) ||
|
||||
usb_endpoint_xfer_int(&ep->desc))
|
||||
ep->ss_ep_comp.wBytesPerInterval =
|
||||
ep->desc.wMaxPacketSize;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ep->ss_ep_comp, desc, USB_DT_SS_EP_COMP_SIZE);
|
||||
|
||||
/* Check the various values */
|
||||
if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
|
||||
dev_warn(ddev, "Control endpoint with bMaxBurst = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to zero\n", desc->bMaxBurst,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bMaxBurst = 0;
|
||||
} else if (desc->bMaxBurst > 15) {
|
||||
dev_warn(ddev, "Endpoint with bMaxBurst = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to 15\n", desc->bMaxBurst,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bMaxBurst = 15;
|
||||
}
|
||||
|
||||
if ((usb_endpoint_xfer_control(&ep->desc) ||
|
||||
usb_endpoint_xfer_int(&ep->desc)) &&
|
||||
desc->bmAttributes != 0) {
|
||||
dev_warn(ddev, "%s endpoint with bmAttributes = %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to zero\n",
|
||||
usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
|
||||
desc->bmAttributes,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bmAttributes = 0;
|
||||
} else if (usb_endpoint_xfer_bulk(&ep->desc) &&
|
||||
desc->bmAttributes > 16) {
|
||||
dev_warn(ddev, "Bulk endpoint with more than 65536 streams in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to max\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bmAttributes = 16;
|
||||
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
||||
desc->bmAttributes > 2) {
|
||||
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to 3\n", desc->bmAttributes + 1,
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bmAttributes = 2;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) *
|
||||
usb_endpoint_maxp(&ep->desc);
|
||||
else if (usb_endpoint_xfer_int(&ep->desc))
|
||||
max_tx = usb_endpoint_maxp(&ep->desc) *
|
||||
(desc->bMaxBurst + 1);
|
||||
else
|
||||
max_tx = 999999;
|
||||
if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) {
|
||||
dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to %d\n",
|
||||
usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
|
||||
le16_to_cpu(desc->wBytesPerInterval),
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress,
|
||||
max_tx);
|
||||
ep->ss_ep_comp.wBytesPerInterval = cpu_to_le16(max_tx);
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
||||
int asnum, struct usb_host_interface *ifp, int num_ep,
|
||||
unsigned char *buffer, int size)
|
||||
{
|
||||
unsigned char *buffer0 = buffer;
|
||||
struct usb_endpoint_descriptor *d;
|
||||
struct usb_host_endpoint *endpoint;
|
||||
int n, i, j, retval;
|
||||
|
||||
d = (struct usb_endpoint_descriptor *) buffer;
|
||||
buffer += d->bLength;
|
||||
size -= d->bLength;
|
||||
|
||||
if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
|
||||
n = USB_DT_ENDPOINT_AUDIO_SIZE;
|
||||
else if (d->bLength >= USB_DT_ENDPOINT_SIZE)
|
||||
n = USB_DT_ENDPOINT_SIZE;
|
||||
else {
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d has an "
|
||||
"invalid endpoint descriptor of length %d, skipping\n",
|
||||
cfgno, inum, asnum, d->bLength);
|
||||
goto skip_to_next_endpoint_or_interface_descriptor;
|
||||
}
|
||||
|
||||
i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
|
||||
if (i >= 16 || i == 0) {
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d has an "
|
||||
"invalid endpoint with address 0x%X, skipping\n",
|
||||
cfgno, inum, asnum, d->bEndpointAddress);
|
||||
goto skip_to_next_endpoint_or_interface_descriptor;
|
||||
}
|
||||
|
||||
/* Only store as many endpoints as we have room for */
|
||||
if (ifp->desc.bNumEndpoints >= num_ep)
|
||||
goto skip_to_next_endpoint_or_interface_descriptor;
|
||||
|
||||
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
|
||||
++ifp->desc.bNumEndpoints;
|
||||
|
||||
memcpy(&endpoint->desc, d, n);
|
||||
INIT_LIST_HEAD(&endpoint->urb_list);
|
||||
|
||||
/* Fix up bInterval values outside the legal range. Use 32 ms if no
|
||||
* proper value can be guessed. */
|
||||
i = 0; /* i = min, j = max, n = default */
|
||||
j = 255;
|
||||
if (usb_endpoint_xfer_int(d)) {
|
||||
i = 1;
|
||||
switch (to_usb_device(ddev)->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_HIGH:
|
||||
/* Many device manufacturers are using full-speed
|
||||
* bInterval values in high-speed interrupt endpoint
|
||||
* descriptors. Try to fix those and fall back to a
|
||||
* 32 ms default value otherwise. */
|
||||
n = fls(d->bInterval*8);
|
||||
if (n == 0)
|
||||
n = 9; /* 32 ms = 2^(9-1) uframes */
|
||||
j = 16;
|
||||
|
||||
/*
|
||||
* Adjust bInterval for quirked devices.
|
||||
* This quirk fixes bIntervals reported in
|
||||
* linear microframes.
|
||||
*/
|
||||
if (to_usb_device(ddev)->quirks &
|
||||
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) {
|
||||
n = clamp(fls(d->bInterval), i, j);
|
||||
i = j = n;
|
||||
}
|
||||
break;
|
||||
default: /* USB_SPEED_FULL or _LOW */
|
||||
/* For low-speed, 10 ms is the official minimum.
|
||||
* But some "overclocked" devices might want faster
|
||||
* polling so we'll allow it. */
|
||||
n = 32;
|
||||
break;
|
||||
}
|
||||
} else if (usb_endpoint_xfer_isoc(d)) {
|
||||
i = 1;
|
||||
j = 16;
|
||||
switch (to_usb_device(ddev)->speed) {
|
||||
case USB_SPEED_HIGH:
|
||||
n = 9; /* 32 ms = 2^(9-1) uframes */
|
||||
break;
|
||||
default: /* USB_SPEED_FULL */
|
||||
n = 6; /* 32 ms = 2^(6-1) frames */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (d->bInterval < i || d->bInterval > j) {
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d "
|
||||
"endpoint 0x%X has an invalid bInterval %d, "
|
||||
"changing to %d\n",
|
||||
cfgno, inum, asnum,
|
||||
d->bEndpointAddress, d->bInterval, n);
|
||||
endpoint->desc.bInterval = n;
|
||||
}
|
||||
|
||||
/* Some buggy low-speed devices have Bulk endpoints, which is
|
||||
* explicitly forbidden by the USB spec. In an attempt to make
|
||||
* them usable, we will try treating them as Interrupt endpoints.
|
||||
*/
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_LOW &&
|
||||
usb_endpoint_xfer_bulk(d)) {
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d "
|
||||
"endpoint 0x%X is Bulk; changing to Interrupt\n",
|
||||
cfgno, inum, asnum, d->bEndpointAddress);
|
||||
endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
|
||||
endpoint->desc.bInterval = 1;
|
||||
if (usb_endpoint_maxp(&endpoint->desc) > 8)
|
||||
endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some buggy high speed devices have bulk endpoints using
|
||||
* maxpacket sizes other than 512. High speed HCDs may not
|
||||
* be able to handle that particular bug, so let's warn...
|
||||
*/
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_HIGH
|
||||
&& usb_endpoint_xfer_bulk(d)) {
|
||||
unsigned maxp;
|
||||
|
||||
maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff;
|
||||
if (maxp != 512)
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d "
|
||||
"bulk endpoint 0x%X has invalid maxpacket %d\n",
|
||||
cfgno, inum, asnum, d->bEndpointAddress,
|
||||
maxp);
|
||||
}
|
||||
|
||||
/* Parse a possible SuperSpeed endpoint companion descriptor */
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_SUPER)
|
||||
usb_parse_ss_endpoint_companion(ddev, cfgno,
|
||||
inum, asnum, endpoint, buffer, size);
|
||||
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the next endpoint or interface descriptor */
|
||||
endpoint->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &n);
|
||||
endpoint->extralen = i;
|
||||
retval = buffer - buffer0 + i;
|
||||
if (n > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
n, plural(n), "endpoint");
|
||||
return retval;
|
||||
|
||||
skip_to_next_endpoint_or_interface_descriptor:
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, NULL);
|
||||
return buffer - buffer0 + i;
|
||||
}
|
||||
|
||||
void usb_release_interface_cache(struct kref *ref)
|
||||
{
|
||||
struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref);
|
||||
int j;
|
||||
|
||||
for (j = 0; j < intfc->num_altsetting; j++) {
|
||||
struct usb_host_interface *alt = &intfc->altsetting[j];
|
||||
|
||||
kfree(alt->endpoint);
|
||||
kfree(alt->string);
|
||||
}
|
||||
kfree(intfc);
|
||||
}
|
||||
|
||||
static int usb_parse_interface(struct device *ddev, int cfgno,
|
||||
struct usb_host_config *config, unsigned char *buffer, int size,
|
||||
u8 inums[], u8 nalts[])
|
||||
{
|
||||
unsigned char *buffer0 = buffer;
|
||||
struct usb_interface_descriptor *d;
|
||||
int inum, asnum;
|
||||
struct usb_interface_cache *intfc;
|
||||
struct usb_host_interface *alt;
|
||||
int i, n;
|
||||
int len, retval;
|
||||
int num_ep, num_ep_orig;
|
||||
|
||||
d = (struct usb_interface_descriptor *) buffer;
|
||||
buffer += d->bLength;
|
||||
size -= d->bLength;
|
||||
|
||||
if (d->bLength < USB_DT_INTERFACE_SIZE)
|
||||
goto skip_to_next_interface_descriptor;
|
||||
|
||||
/* Which interface entry is this? */
|
||||
intfc = NULL;
|
||||
inum = d->bInterfaceNumber;
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
if (inums[i] == inum) {
|
||||
intfc = config->intf_cache[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!intfc || intfc->num_altsetting >= nalts[i])
|
||||
goto skip_to_next_interface_descriptor;
|
||||
|
||||
/* Check for duplicate altsetting entries */
|
||||
asnum = d->bAlternateSetting;
|
||||
for ((i = 0, alt = &intfc->altsetting[0]);
|
||||
i < intfc->num_altsetting;
|
||||
(++i, ++alt)) {
|
||||
if (alt->desc.bAlternateSetting == asnum) {
|
||||
dev_warn(ddev, "Duplicate descriptor for config %d "
|
||||
"interface %d altsetting %d, skipping\n",
|
||||
cfgno, inum, asnum);
|
||||
goto skip_to_next_interface_descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
++intfc->num_altsetting;
|
||||
memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE);
|
||||
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the first endpoint or interface descriptor */
|
||||
alt->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
||||
USB_DT_INTERFACE, &n);
|
||||
alt->extralen = i;
|
||||
if (n > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
n, plural(n), "interface");
|
||||
buffer += i;
|
||||
size -= i;
|
||||
|
||||
/* Allocate space for the right(?) number of endpoints */
|
||||
num_ep = num_ep_orig = alt->desc.bNumEndpoints;
|
||||
alt->desc.bNumEndpoints = 0; /* Use as a counter */
|
||||
if (num_ep > USB_MAXENDPOINTS) {
|
||||
dev_warn(ddev, "too many endpoints for config %d interface %d "
|
||||
"altsetting %d: %d, using maximum allowed: %d\n",
|
||||
cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS);
|
||||
num_ep = USB_MAXENDPOINTS;
|
||||
}
|
||||
|
||||
if (num_ep > 0) {
|
||||
/* Can't allocate 0 bytes */
|
||||
len = sizeof(struct usb_host_endpoint) * num_ep;
|
||||
alt->endpoint = kzalloc(len, GFP_KERNEL);
|
||||
if (!alt->endpoint)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Parse all the endpoint descriptors */
|
||||
n = 0;
|
||||
while (size > 0) {
|
||||
if (((struct usb_descriptor_header *) buffer)->bDescriptorType
|
||||
== USB_DT_INTERFACE)
|
||||
break;
|
||||
retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,
|
||||
num_ep, buffer, size);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
++n;
|
||||
|
||||
buffer += retval;
|
||||
size -= retval;
|
||||
}
|
||||
|
||||
if (n != num_ep_orig)
|
||||
dev_warn(ddev, "config %d interface %d altsetting %d has %d "
|
||||
"endpoint descriptor%s, different from the interface "
|
||||
"descriptor's value: %d\n",
|
||||
cfgno, inum, asnum, n, plural(n), num_ep_orig);
|
||||
return buffer - buffer0;
|
||||
|
||||
skip_to_next_interface_descriptor:
|
||||
i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
|
||||
USB_DT_INTERFACE, NULL);
|
||||
return buffer - buffer0 + i;
|
||||
}
|
||||
|
||||
static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
|
||||
struct usb_host_config *config, unsigned char *buffer, int size)
|
||||
{
|
||||
struct device *ddev = &dev->dev;
|
||||
unsigned char *buffer0 = buffer;
|
||||
int cfgno;
|
||||
int nintf, nintf_orig;
|
||||
int i, j, n;
|
||||
struct usb_interface_cache *intfc;
|
||||
unsigned char *buffer2;
|
||||
int size2;
|
||||
struct usb_descriptor_header *header;
|
||||
int len, retval;
|
||||
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
|
||||
unsigned iad_num = 0;
|
||||
|
||||
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
|
||||
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
|
||||
config->desc.bLength < USB_DT_CONFIG_SIZE ||
|
||||
config->desc.bLength > size) {
|
||||
dev_err(ddev, "invalid descriptor for config index %d: "
|
||||
"type = 0x%X, length = %d\n", cfgidx,
|
||||
config->desc.bDescriptorType, config->desc.bLength);
|
||||
return -EINVAL;
|
||||
}
|
||||
cfgno = config->desc.bConfigurationValue;
|
||||
|
||||
buffer += config->desc.bLength;
|
||||
size -= config->desc.bLength;
|
||||
|
||||
nintf = nintf_orig = config->desc.bNumInterfaces;
|
||||
if (nintf > USB_MAXINTERFACES) {
|
||||
dev_warn(ddev, "config %d has too many interfaces: %d, "
|
||||
"using maximum allowed: %d\n",
|
||||
cfgno, nintf, USB_MAXINTERFACES);
|
||||
nintf = USB_MAXINTERFACES;
|
||||
}
|
||||
|
||||
/* Go through the descriptors, checking their length and counting the
|
||||
* number of altsettings for each interface */
|
||||
n = 0;
|
||||
for ((buffer2 = buffer, size2 = size);
|
||||
size2 > 0;
|
||||
(buffer2 += header->bLength, size2 -= header->bLength)) {
|
||||
|
||||
if (size2 < sizeof(struct usb_descriptor_header)) {
|
||||
dev_warn(ddev, "config %d descriptor has %d excess "
|
||||
"byte%s, ignoring\n",
|
||||
cfgno, size2, plural(size2));
|
||||
break;
|
||||
}
|
||||
|
||||
header = (struct usb_descriptor_header *) buffer2;
|
||||
if ((header->bLength > size2) || (header->bLength < 2)) {
|
||||
dev_warn(ddev, "config %d has an invalid descriptor "
|
||||
"of length %d, skipping remainder of the config\n",
|
||||
cfgno, header->bLength);
|
||||
break;
|
||||
}
|
||||
|
||||
if (header->bDescriptorType == USB_DT_INTERFACE) {
|
||||
struct usb_interface_descriptor *d;
|
||||
int inum;
|
||||
|
||||
d = (struct usb_interface_descriptor *) header;
|
||||
if (d->bLength < USB_DT_INTERFACE_SIZE) {
|
||||
dev_warn(ddev, "config %d has an invalid "
|
||||
"interface descriptor of length %d, "
|
||||
"skipping\n", cfgno, d->bLength);
|
||||
continue;
|
||||
}
|
||||
|
||||
inum = d->bInterfaceNumber;
|
||||
|
||||
if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&
|
||||
n >= nintf_orig) {
|
||||
dev_warn(ddev, "config %d has more interface "
|
||||
"descriptors, than it declares in "
|
||||
"bNumInterfaces, ignoring interface "
|
||||
"number: %d\n", cfgno, inum);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inum >= nintf_orig)
|
||||
dev_warn(ddev, "config %d has an invalid "
|
||||
"interface number: %d but max is %d\n",
|
||||
cfgno, inum, nintf_orig - 1);
|
||||
|
||||
/* Have we already encountered this interface?
|
||||
* Count its altsettings */
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (inums[i] == inum)
|
||||
break;
|
||||
}
|
||||
if (i < n) {
|
||||
if (nalts[i] < 255)
|
||||
++nalts[i];
|
||||
} else if (n < USB_MAXINTERFACES) {
|
||||
inums[n] = inum;
|
||||
nalts[n] = 1;
|
||||
++n;
|
||||
}
|
||||
|
||||
} else if (header->bDescriptorType ==
|
||||
USB_DT_INTERFACE_ASSOCIATION) {
|
||||
if (iad_num == USB_MAXIADS) {
|
||||
dev_warn(ddev, "found more Interface "
|
||||
"Association Descriptors "
|
||||
"than allocated for in "
|
||||
"configuration %d\n", cfgno);
|
||||
} else {
|
||||
config->intf_assoc[iad_num] =
|
||||
(struct usb_interface_assoc_descriptor
|
||||
*)header;
|
||||
iad_num++;
|
||||
}
|
||||
|
||||
} else if (header->bDescriptorType == USB_DT_DEVICE ||
|
||||
header->bDescriptorType == USB_DT_CONFIG)
|
||||
dev_warn(ddev, "config %d contains an unexpected "
|
||||
"descriptor of type 0x%X, skipping\n",
|
||||
cfgno, header->bDescriptorType);
|
||||
|
||||
} /* for ((buffer2 = buffer, size2 = size); ...) */
|
||||
size = buffer2 - buffer;
|
||||
config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);
|
||||
|
||||
if (n != nintf)
|
||||
dev_warn(ddev, "config %d has %d interface%s, different from "
|
||||
"the descriptor's value: %d\n",
|
||||
cfgno, n, plural(n), nintf_orig);
|
||||
else if (n == 0)
|
||||
dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
|
||||
config->desc.bNumInterfaces = nintf = n;
|
||||
|
||||
/* Check for missing interface numbers */
|
||||
for (i = 0; i < nintf; ++i) {
|
||||
for (j = 0; j < nintf; ++j) {
|
||||
if (inums[j] == i)
|
||||
break;
|
||||
}
|
||||
if (j >= nintf)
|
||||
dev_warn(ddev, "config %d has no interface number "
|
||||
"%d\n", cfgno, i);
|
||||
}
|
||||
|
||||
/* Allocate the usb_interface_caches and altsetting arrays */
|
||||
for (i = 0; i < nintf; ++i) {
|
||||
j = nalts[i];
|
||||
if (j > USB_MAXALTSETTING) {
|
||||
dev_warn(ddev, "too many alternate settings for "
|
||||
"config %d interface %d: %d, "
|
||||
"using maximum allowed: %d\n",
|
||||
cfgno, inums[i], j, USB_MAXALTSETTING);
|
||||
nalts[i] = j = USB_MAXALTSETTING;
|
||||
}
|
||||
|
||||
len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
|
||||
config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL);
|
||||
if (!intfc)
|
||||
return -ENOMEM;
|
||||
kref_init(&intfc->ref);
|
||||
}
|
||||
|
||||
/* FIXME: parse the BOS descriptor */
|
||||
|
||||
/* Skip over any Class Specific or Vendor Specific descriptors;
|
||||
* find the first interface descriptor */
|
||||
config->extra = buffer;
|
||||
i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
|
||||
USB_DT_INTERFACE, &n);
|
||||
config->extralen = i;
|
||||
if (n > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
n, plural(n), "configuration");
|
||||
buffer += i;
|
||||
size -= i;
|
||||
|
||||
/* Parse all the interface/altsetting descriptors */
|
||||
while (size > 0) {
|
||||
retval = usb_parse_interface(ddev, cfgno, config,
|
||||
buffer, size, inums, nalts);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
buffer += retval;
|
||||
size -= retval;
|
||||
}
|
||||
|
||||
/* Check for missing altsettings */
|
||||
for (i = 0; i < nintf; ++i) {
|
||||
intfc = config->intf_cache[i];
|
||||
for (j = 0; j < intfc->num_altsetting; ++j) {
|
||||
for (n = 0; n < intfc->num_altsetting; ++n) {
|
||||
if (intfc->altsetting[n].desc.
|
||||
bAlternateSetting == j)
|
||||
break;
|
||||
}
|
||||
if (n >= intfc->num_altsetting)
|
||||
dev_warn(ddev, "config %d interface %d has no "
|
||||
"altsetting %d\n", cfgno, inums[i], j);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hub-only!! ... and only exported for reset/reinit path.
|
||||
* otherwise used internally on disconnect/destroy path
|
||||
*/
|
||||
void usb_destroy_configuration(struct usb_device *dev)
|
||||
{
|
||||
int c, i;
|
||||
|
||||
if (!dev->config)
|
||||
return;
|
||||
|
||||
if (dev->rawdescriptors) {
|
||||
for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
|
||||
kfree(dev->rawdescriptors[i]);
|
||||
|
||||
kfree(dev->rawdescriptors);
|
||||
dev->rawdescriptors = NULL;
|
||||
}
|
||||
|
||||
for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
|
||||
struct usb_host_config *cf = &dev->config[c];
|
||||
|
||||
kfree(cf->string);
|
||||
for (i = 0; i < cf->desc.bNumInterfaces; i++) {
|
||||
if (cf->intf_cache[i])
|
||||
kref_put(&cf->intf_cache[i]->ref,
|
||||
usb_release_interface_cache);
|
||||
}
|
||||
}
|
||||
kfree(dev->config);
|
||||
dev->config = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the USB config descriptors, cache and parse'em
|
||||
*
|
||||
* hub-only!! ... and only in reset path, or usb_new_device()
|
||||
* (used by real hubs and virtual root hubs)
|
||||
*/
|
||||
int usb_get_configuration(struct usb_device *dev)
|
||||
{
|
||||
struct device *ddev = &dev->dev;
|
||||
int ncfg = dev->descriptor.bNumConfigurations;
|
||||
int result = 0;
|
||||
unsigned int cfgno, length;
|
||||
unsigned char *bigbuffer;
|
||||
struct usb_config_descriptor *desc;
|
||||
|
||||
cfgno = 0;
|
||||
result = -ENOMEM;
|
||||
if (ncfg > USB_MAXCONFIG) {
|
||||
dev_warn(ddev, "too many configurations: %d, "
|
||||
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
|
||||
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
|
||||
}
|
||||
|
||||
if (ncfg < 1) {
|
||||
dev_err(ddev, "no configurations\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
length = ncfg * sizeof(struct usb_host_config);
|
||||
dev->config = kzalloc(length, GFP_KERNEL);
|
||||
if (!dev->config)
|
||||
goto err2;
|
||||
|
||||
length = ncfg * sizeof(char *);
|
||||
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
|
||||
if (!dev->rawdescriptors)
|
||||
goto err2;
|
||||
|
||||
desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto err2;
|
||||
|
||||
result = 0;
|
||||
for (; cfgno < ncfg; cfgno++) {
|
||||
/* We grab just the first descriptor so we know how long
|
||||
* the whole configuration is */
|
||||
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
|
||||
desc, USB_DT_CONFIG_SIZE);
|
||||
if (result < 0) {
|
||||
dev_err(ddev, "unable to read config index %d "
|
||||
"descriptor/%s: %d\n", cfgno, "start", result);
|
||||
if (result != -EPIPE)
|
||||
goto err;
|
||||
dev_err(ddev, "chopping to %d config(s)\n", cfgno);
|
||||
dev->descriptor.bNumConfigurations = cfgno;
|
||||
break;
|
||||
} else if (result < 4) {
|
||||
dev_err(ddev, "config index %d descriptor too short "
|
||||
"(expected %i, got %i)\n", cfgno,
|
||||
USB_DT_CONFIG_SIZE, result);
|
||||
result = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
length = max((int) le16_to_cpu(desc->wTotalLength),
|
||||
USB_DT_CONFIG_SIZE);
|
||||
|
||||
/* Now that we know the length, get the whole thing */
|
||||
bigbuffer = kmalloc(length, GFP_KERNEL);
|
||||
if (!bigbuffer) {
|
||||
result = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dev->quirks & USB_QUIRK_DELAY_INIT)
|
||||
msleep(100);
|
||||
|
||||
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
|
||||
bigbuffer, length);
|
||||
if (result < 0) {
|
||||
dev_err(ddev, "unable to read config index %d "
|
||||
"descriptor/%s\n", cfgno, "all");
|
||||
kfree(bigbuffer);
|
||||
goto err;
|
||||
}
|
||||
if (result < length) {
|
||||
dev_warn(ddev, "config index %d descriptor too short "
|
||||
"(expected %i, got %i)\n", cfgno, length, result);
|
||||
length = result;
|
||||
}
|
||||
|
||||
dev->rawdescriptors[cfgno] = bigbuffer;
|
||||
|
||||
result = usb_parse_configuration(dev, cfgno,
|
||||
&dev->config[cfgno], bigbuffer, length);
|
||||
if (result < 0) {
|
||||
++cfgno;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
result = 0;
|
||||
|
||||
err:
|
||||
kfree(desc);
|
||||
dev->descriptor.bNumConfigurations = cfgno;
|
||||
err2:
|
||||
if (result == -ENOMEM)
|
||||
dev_err(ddev, "out of memory\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
void usb_release_bos_descriptor(struct usb_device *dev)
|
||||
{
|
||||
if (dev->bos) {
|
||||
kfree(dev->bos->desc);
|
||||
kfree(dev->bos);
|
||||
dev->bos = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get BOS descriptor set */
|
||||
int usb_get_bos_descriptor(struct usb_device *dev)
|
||||
{
|
||||
struct device *ddev = &dev->dev;
|
||||
struct usb_bos_descriptor *bos;
|
||||
struct usb_dev_cap_header *cap;
|
||||
unsigned char *buffer;
|
||||
int length, total_len, num, i;
|
||||
int ret;
|
||||
|
||||
bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL);
|
||||
if (!bos)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get BOS descriptor */
|
||||
ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE);
|
||||
if (ret < USB_DT_BOS_SIZE) {
|
||||
dev_err(ddev, "unable to get BOS descriptor\n");
|
||||
if (ret >= 0)
|
||||
ret = -ENOMSG;
|
||||
kfree(bos);
|
||||
return ret;
|
||||
}
|
||||
|
||||
length = bos->bLength;
|
||||
total_len = le16_to_cpu(bos->wTotalLength);
|
||||
num = bos->bNumDeviceCaps;
|
||||
kfree(bos);
|
||||
if (total_len < length)
|
||||
return -EINVAL;
|
||||
|
||||
dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL);
|
||||
if (!dev->bos)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Now let's get the whole BOS descriptor set */
|
||||
buffer = kzalloc(total_len, GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
dev->bos->desc = (struct usb_bos_descriptor *)buffer;
|
||||
|
||||
ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len);
|
||||
if (ret < total_len) {
|
||||
dev_err(ddev, "unable to get BOS descriptor set\n");
|
||||
if (ret >= 0)
|
||||
ret = -ENOMSG;
|
||||
goto err;
|
||||
}
|
||||
total_len -= length;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
buffer += length;
|
||||
cap = (struct usb_dev_cap_header *)buffer;
|
||||
length = cap->bLength;
|
||||
|
||||
if (total_len < length)
|
||||
break;
|
||||
total_len -= length;
|
||||
|
||||
if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
|
||||
dev_warn(ddev, "descriptor type invalid, skip\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (cap->bDevCapabilityType) {
|
||||
case USB_CAP_TYPE_WIRELESS_USB:
|
||||
/* Wireless USB cap descriptor is handled by wusb */
|
||||
break;
|
||||
case USB_CAP_TYPE_EXT:
|
||||
dev->bos->ext_cap =
|
||||
(struct usb_ext_cap_descriptor *)buffer;
|
||||
break;
|
||||
case USB_SS_CAP_TYPE:
|
||||
dev->bos->ss_cap =
|
||||
(struct usb_ss_cap_descriptor *)buffer;
|
||||
break;
|
||||
case CONTAINER_ID_TYPE:
|
||||
dev->bos->ss_id =
|
||||
(struct usb_ss_container_id_descriptor *)buffer;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
usb_release_bos_descriptor(dev);
|
||||
return ret;
|
||||
}
|
692
drivers/usb/core/devices.c
Normal file
692
drivers/usb/core/devices.c
Normal file
|
@ -0,0 +1,692 @@
|
|||
/*
|
||||
* devices.c
|
||||
* (C) Copyright 1999 Randy Dunlap.
|
||||
* (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>.
|
||||
* (proc file per device)
|
||||
* (C) Copyright 1999 Deti Fliegl (new USB architecture)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*************************************************************
|
||||
*
|
||||
* <mountpoint>/devices contains USB topology, device, config, class,
|
||||
* interface, & endpoint data.
|
||||
*
|
||||
* I considered using /proc/bus/usb/devices/device# for each device
|
||||
* as it is attached or detached, but I didn't like this for some
|
||||
* reason -- maybe it's just too deep of a directory structure.
|
||||
* I also don't like looking in multiple places to gather and view
|
||||
* the data. Having only one file for ./devices also prevents race
|
||||
* conditions that could arise if a program was reading device info
|
||||
* for devices that are being removed (unplugged). (That is, the
|
||||
* program may find a directory for devnum_12 then try to open it,
|
||||
* but it was just unplugged, so the directory is now deleted.
|
||||
* But programs would just have to be prepared for situations like
|
||||
* this in any plug-and-play environment.)
|
||||
*
|
||||
* 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
|
||||
* Converted the whole proc stuff to real
|
||||
* read methods. Now not the whole device list needs to fit
|
||||
* into one page, only the device list for one bus.
|
||||
* Added a poll method to /proc/bus/usb/devices, to wake
|
||||
* up an eventual usbd
|
||||
* 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
|
||||
* Turned into its own filesystem
|
||||
* 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
|
||||
* Converted file reading routine to dump to buffer once
|
||||
* per device, not per bus
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
|
||||
#define ALLOW_SERIAL_NUMBER
|
||||
|
||||
static const char format_topo[] =
|
||||
/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */
|
||||
"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n";
|
||||
|
||||
static const char format_string_manufacturer[] =
|
||||
/* S: Manufacturer=xxxx */
|
||||
"S: Manufacturer=%.100s\n";
|
||||
|
||||
static const char format_string_product[] =
|
||||
/* S: Product=xxxx */
|
||||
"S: Product=%.100s\n";
|
||||
|
||||
#ifdef ALLOW_SERIAL_NUMBER
|
||||
static const char format_string_serialnumber[] =
|
||||
/* S: SerialNumber=xxxx */
|
||||
"S: SerialNumber=%.100s\n";
|
||||
#endif
|
||||
|
||||
static const char format_bandwidth[] =
|
||||
/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
|
||||
"B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
|
||||
|
||||
static const char format_device1[] =
|
||||
/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
|
||||
"D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
|
||||
|
||||
static const char format_device2[] =
|
||||
/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */
|
||||
"P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
|
||||
|
||||
static const char format_config[] =
|
||||
/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
|
||||
"C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
|
||||
|
||||
static const char format_iad[] =
|
||||
/* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
|
||||
"A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n";
|
||||
|
||||
static const char format_iface[] =
|
||||
/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
|
||||
"I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
|
||||
|
||||
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
|
||||
* the event counter by two, so it will always _stay_ odd. That means
|
||||
* that it will never be zero, so "event 0" will never match a current
|
||||
* event, and thus 'poll' will always trigger as readable for the first
|
||||
* time it gets called.
|
||||
*/
|
||||
static struct device_connect_event {
|
||||
atomic_t count;
|
||||
wait_queue_head_t wait;
|
||||
} device_event = {
|
||||
.count = ATOMIC_INIT(1),
|
||||
.wait = __WAIT_QUEUE_HEAD_INITIALIZER(device_event.wait)
|
||||
};
|
||||
|
||||
struct class_info {
|
||||
int class;
|
||||
char *class_name;
|
||||
};
|
||||
|
||||
static const struct class_info clas_info[] = {
|
||||
/* max. 5 chars. per name string */
|
||||
{USB_CLASS_PER_INTERFACE, ">ifc"},
|
||||
{USB_CLASS_AUDIO, "audio"},
|
||||
{USB_CLASS_COMM, "comm."},
|
||||
{USB_CLASS_HID, "HID"},
|
||||
{USB_CLASS_PHYSICAL, "PID"},
|
||||
{USB_CLASS_STILL_IMAGE, "still"},
|
||||
{USB_CLASS_PRINTER, "print"},
|
||||
{USB_CLASS_MASS_STORAGE, "stor."},
|
||||
{USB_CLASS_HUB, "hub"},
|
||||
{USB_CLASS_CDC_DATA, "data"},
|
||||
{USB_CLASS_CSCID, "scard"},
|
||||
{USB_CLASS_CONTENT_SEC, "c-sec"},
|
||||
{USB_CLASS_VIDEO, "video"},
|
||||
{USB_CLASS_WIRELESS_CONTROLLER, "wlcon"},
|
||||
{USB_CLASS_MISC, "misc"},
|
||||
{USB_CLASS_APP_SPEC, "app."},
|
||||
{USB_CLASS_VENDOR_SPEC, "vend."},
|
||||
{-1, "unk."} /* leave as last */
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
void usbfs_conn_disc_event(void)
|
||||
{
|
||||
atomic_add(2, &device_event.count);
|
||||
wake_up(&device_event.wait);
|
||||
}
|
||||
|
||||
static const char *class_decode(const int class)
|
||||
{
|
||||
int ix;
|
||||
|
||||
for (ix = 0; clas_info[ix].class != -1; ix++)
|
||||
if (clas_info[ix].class == class)
|
||||
break;
|
||||
return clas_info[ix].class_name;
|
||||
}
|
||||
|
||||
static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
char dir, unit, *type;
|
||||
unsigned interval, bandwidth = 1;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
|
||||
dir = usb_endpoint_dir_in(desc) ? 'I' : 'O';
|
||||
|
||||
if (speed == USB_SPEED_HIGH) {
|
||||
switch (usb_endpoint_maxp(desc) & (0x03 << 11)) {
|
||||
case 1 << 11:
|
||||
bandwidth = 2; break;
|
||||
case 2 << 11:
|
||||
bandwidth = 3; break;
|
||||
}
|
||||
}
|
||||
|
||||
/* this isn't checking for illegal values */
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
type = "Ctrl";
|
||||
if (speed == USB_SPEED_HIGH) /* uframes per NAK */
|
||||
interval = desc->bInterval;
|
||||
else
|
||||
interval = 0;
|
||||
dir = 'B'; /* ctrl is bidirectional */
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
type = "Isoc";
|
||||
interval = 1 << (desc->bInterval - 1);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
type = "Bulk";
|
||||
if (speed == USB_SPEED_HIGH && dir == 'O') /* uframes per NAK */
|
||||
interval = desc->bInterval;
|
||||
else
|
||||
interval = 0;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
type = "Int.";
|
||||
if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER)
|
||||
interval = 1 << (desc->bInterval - 1);
|
||||
else
|
||||
interval = desc->bInterval;
|
||||
break;
|
||||
default: /* "can't happen" */
|
||||
return start;
|
||||
}
|
||||
interval *= (speed == USB_SPEED_HIGH ||
|
||||
speed == USB_SPEED_SUPER) ? 125 : 1000;
|
||||
if (interval % 1000)
|
||||
unit = 'u';
|
||||
else {
|
||||
unit = 'm';
|
||||
interval /= 1000;
|
||||
}
|
||||
|
||||
start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
|
||||
desc->bmAttributes, type,
|
||||
(usb_endpoint_maxp(desc) & 0x07ff) *
|
||||
bandwidth,
|
||||
interval, unit);
|
||||
return start;
|
||||
}
|
||||
|
||||
static char *usb_dump_interface_descriptor(char *start, char *end,
|
||||
const struct usb_interface_cache *intfc,
|
||||
const struct usb_interface *iface,
|
||||
int setno)
|
||||
{
|
||||
const struct usb_interface_descriptor *desc;
|
||||
const char *driver_name = "";
|
||||
int active = 0;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
desc = &intfc->altsetting[setno].desc;
|
||||
if (iface) {
|
||||
driver_name = (iface->dev.driver
|
||||
? iface->dev.driver->name
|
||||
: "(none)");
|
||||
active = (desc == &iface->cur_altsetting->desc);
|
||||
}
|
||||
start += sprintf(start, format_iface,
|
||||
active ? '*' : ' ', /* mark active altsetting */
|
||||
desc->bInterfaceNumber,
|
||||
desc->bAlternateSetting,
|
||||
desc->bNumEndpoints,
|
||||
desc->bInterfaceClass,
|
||||
class_decode(desc->bInterfaceClass),
|
||||
desc->bInterfaceSubClass,
|
||||
desc->bInterfaceProtocol,
|
||||
driver_name);
|
||||
return start;
|
||||
}
|
||||
|
||||
static char *usb_dump_interface(int speed, char *start, char *end,
|
||||
const struct usb_interface_cache *intfc,
|
||||
const struct usb_interface *iface, int setno)
|
||||
{
|
||||
const struct usb_host_interface *desc = &intfc->altsetting[setno];
|
||||
int i;
|
||||
|
||||
start = usb_dump_interface_descriptor(start, end, intfc, iface, setno);
|
||||
for (i = 0; i < desc->desc.bNumEndpoints; i++) {
|
||||
if (start > end)
|
||||
return start;
|
||||
start = usb_dump_endpoint_descriptor(speed,
|
||||
start, end, &desc->endpoint[i].desc);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
static char *usb_dump_iad_descriptor(char *start, char *end,
|
||||
const struct usb_interface_assoc_descriptor *iad)
|
||||
{
|
||||
if (start > end)
|
||||
return start;
|
||||
start += sprintf(start, format_iad,
|
||||
iad->bFirstInterface,
|
||||
iad->bInterfaceCount,
|
||||
iad->bFunctionClass,
|
||||
class_decode(iad->bFunctionClass),
|
||||
iad->bFunctionSubClass,
|
||||
iad->bFunctionProtocol);
|
||||
return start;
|
||||
}
|
||||
|
||||
/* TBD:
|
||||
* 0. TBDs
|
||||
* 1. marking active interface altsettings (code lists all, but should mark
|
||||
* which ones are active, if any)
|
||||
*/
|
||||
static char *usb_dump_config_descriptor(char *start, char *end,
|
||||
const struct usb_config_descriptor *desc,
|
||||
int active, int speed)
|
||||
{
|
||||
int mul;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
if (speed == USB_SPEED_SUPER)
|
||||
mul = 8;
|
||||
else
|
||||
mul = 2;
|
||||
start += sprintf(start, format_config,
|
||||
/* mark active/actual/current cfg. */
|
||||
active ? '*' : ' ',
|
||||
desc->bNumInterfaces,
|
||||
desc->bConfigurationValue,
|
||||
desc->bmAttributes,
|
||||
desc->bMaxPower * mul);
|
||||
return start;
|
||||
}
|
||||
|
||||
static char *usb_dump_config(int speed, char *start, char *end,
|
||||
const struct usb_host_config *config, int active)
|
||||
{
|
||||
int i, j;
|
||||
struct usb_interface_cache *intfc;
|
||||
struct usb_interface *interface;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
if (!config)
|
||||
/* getting these some in 2.3.7; none in 2.3.6 */
|
||||
return start + sprintf(start, "(null Cfg. desc.)\n");
|
||||
start = usb_dump_config_descriptor(start, end, &config->desc, active,
|
||||
speed);
|
||||
for (i = 0; i < USB_MAXIADS; i++) {
|
||||
if (config->intf_assoc[i] == NULL)
|
||||
break;
|
||||
start = usb_dump_iad_descriptor(start, end,
|
||||
config->intf_assoc[i]);
|
||||
}
|
||||
for (i = 0; i < config->desc.bNumInterfaces; i++) {
|
||||
intfc = config->intf_cache[i];
|
||||
interface = config->interface[i];
|
||||
for (j = 0; j < intfc->num_altsetting; j++) {
|
||||
if (start > end)
|
||||
return start;
|
||||
start = usb_dump_interface(speed,
|
||||
start, end, intfc, interface, j);
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump the different USB descriptors.
|
||||
*/
|
||||
static char *usb_dump_device_descriptor(char *start, char *end,
|
||||
const struct usb_device_descriptor *desc)
|
||||
{
|
||||
u16 bcdUSB = le16_to_cpu(desc->bcdUSB);
|
||||
u16 bcdDevice = le16_to_cpu(desc->bcdDevice);
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
start += sprintf(start, format_device1,
|
||||
bcdUSB >> 8, bcdUSB & 0xff,
|
||||
desc->bDeviceClass,
|
||||
class_decode(desc->bDeviceClass),
|
||||
desc->bDeviceSubClass,
|
||||
desc->bDeviceProtocol,
|
||||
desc->bMaxPacketSize0,
|
||||
desc->bNumConfigurations);
|
||||
if (start > end)
|
||||
return start;
|
||||
start += sprintf(start, format_device2,
|
||||
le16_to_cpu(desc->idVendor),
|
||||
le16_to_cpu(desc->idProduct),
|
||||
bcdDevice >> 8, bcdDevice & 0xff);
|
||||
return start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump the different strings that this device holds.
|
||||
*/
|
||||
static char *usb_dump_device_strings(char *start, char *end,
|
||||
struct usb_device *dev)
|
||||
{
|
||||
if (start > end)
|
||||
return start;
|
||||
if (dev->manufacturer)
|
||||
start += sprintf(start, format_string_manufacturer,
|
||||
dev->manufacturer);
|
||||
if (start > end)
|
||||
goto out;
|
||||
if (dev->product)
|
||||
start += sprintf(start, format_string_product, dev->product);
|
||||
if (start > end)
|
||||
goto out;
|
||||
#ifdef ALLOW_SERIAL_NUMBER
|
||||
if (dev->serial)
|
||||
start += sprintf(start, format_string_serialnumber,
|
||||
dev->serial);
|
||||
#endif
|
||||
out:
|
||||
return start;
|
||||
}
|
||||
|
||||
static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
|
||||
start = usb_dump_device_descriptor(start, end, &dev->descriptor);
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
|
||||
start = usb_dump_device_strings(start, end, dev);
|
||||
|
||||
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
|
||||
if (start > end)
|
||||
return start;
|
||||
start = usb_dump_config(dev->speed,
|
||||
start, end, dev->config + i,
|
||||
/* active ? */
|
||||
(dev->config + i) == dev->actconfig);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PROC_EXTRA /* TBD: may want to add this code later */
|
||||
|
||||
static char *usb_dump_hub_descriptor(char *start, char *end,
|
||||
const struct usb_hub_descriptor *desc)
|
||||
{
|
||||
int leng = USB_DT_HUB_NONVAR_SIZE;
|
||||
unsigned char *ptr = (unsigned char *)desc;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
start += sprintf(start, "Interface:");
|
||||
while (leng && start <= end) {
|
||||
start += sprintf(start, " %02x", *ptr);
|
||||
ptr++; leng--;
|
||||
}
|
||||
*start++ = '\n';
|
||||
return start;
|
||||
}
|
||||
|
||||
static char *usb_dump_string(char *start, char *end,
|
||||
const struct usb_device *dev, char *id, int index)
|
||||
{
|
||||
if (start > end)
|
||||
return start;
|
||||
start += sprintf(start, "Interface:");
|
||||
if (index <= dev->maxstring && dev->stringindex &&
|
||||
dev->stringindex[index])
|
||||
start += sprintf(start, "%s: %.100s ", id,
|
||||
dev->stringindex[index]);
|
||||
return start;
|
||||
}
|
||||
|
||||
#endif /* PROC_EXTRA */
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/* This is a recursive function. Parameters:
|
||||
* buffer - the user-space buffer to write data into
|
||||
* nbytes - the maximum number of bytes to write
|
||||
* skip_bytes - the number of bytes to skip before writing anything
|
||||
* file_offset - the offset into the devices file on completion
|
||||
* The caller must own the device lock.
|
||||
*/
|
||||
static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
||||
loff_t *skip_bytes, loff_t *file_offset,
|
||||
struct usb_device *usbdev, struct usb_bus *bus,
|
||||
int level, int index, int count)
|
||||
{
|
||||
int chix;
|
||||
int ret, cnt = 0;
|
||||
int parent_devnum = 0;
|
||||
char *pages_start, *data_end, *speed;
|
||||
unsigned int length;
|
||||
ssize_t total_written = 0;
|
||||
struct usb_device *childdev = NULL;
|
||||
|
||||
/* don't bother with anything else if we're not writing any data */
|
||||
if (*nbytes <= 0)
|
||||
return 0;
|
||||
|
||||
if (level > MAX_TOPO_LEVEL)
|
||||
return 0;
|
||||
/* allocate 2^1 pages = 8K (on i386);
|
||||
* should be more than enough for one device */
|
||||
pages_start = (char *)__get_free_pages(GFP_NOIO, 1);
|
||||
if (!pages_start)
|
||||
return -ENOMEM;
|
||||
|
||||
if (usbdev->parent && usbdev->parent->devnum != -1)
|
||||
parent_devnum = usbdev->parent->devnum;
|
||||
/*
|
||||
* So the root hub's parent is 0 and any device that is
|
||||
* plugged into the root hub has a parent of 0.
|
||||
*/
|
||||
switch (usbdev->speed) {
|
||||
case USB_SPEED_LOW:
|
||||
speed = "1.5"; break;
|
||||
case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */
|
||||
case USB_SPEED_FULL:
|
||||
speed = "12"; break;
|
||||
case USB_SPEED_WIRELESS: /* Wireless has no real fixed speed */
|
||||
case USB_SPEED_HIGH:
|
||||
speed = "480"; break;
|
||||
case USB_SPEED_SUPER:
|
||||
speed = "5000"; break;
|
||||
default:
|
||||
speed = "??";
|
||||
}
|
||||
data_end = pages_start + sprintf(pages_start, format_topo,
|
||||
bus->busnum, level, parent_devnum,
|
||||
index, count, usbdev->devnum,
|
||||
speed, usbdev->maxchild);
|
||||
/*
|
||||
* level = topology-tier level;
|
||||
* parent_devnum = parent device number;
|
||||
* index = parent's connector number;
|
||||
* count = device count at this level
|
||||
*/
|
||||
/* If this is the root hub, display the bandwidth information */
|
||||
if (level == 0) {
|
||||
int max;
|
||||
|
||||
/* super/high speed reserves 80%, full/low reserves 90% */
|
||||
if (usbdev->speed == USB_SPEED_HIGH ||
|
||||
usbdev->speed == USB_SPEED_SUPER)
|
||||
max = 800;
|
||||
else
|
||||
max = FRAME_TIME_MAX_USECS_ALLOC;
|
||||
|
||||
/* report "average" periodic allocation over a microsecond.
|
||||
* the schedules are actually bursty, HCDs need to deal with
|
||||
* that and just compute/report this average.
|
||||
*/
|
||||
data_end += sprintf(data_end, format_bandwidth,
|
||||
bus->bandwidth_allocated, max,
|
||||
(100 * bus->bandwidth_allocated + max / 2)
|
||||
/ max,
|
||||
bus->bandwidth_int_reqs,
|
||||
bus->bandwidth_isoc_reqs);
|
||||
|
||||
}
|
||||
data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256,
|
||||
usbdev);
|
||||
|
||||
if (data_end > (pages_start + (2 * PAGE_SIZE) - 256))
|
||||
data_end += sprintf(data_end, "(truncated)\n");
|
||||
|
||||
length = data_end - pages_start;
|
||||
/* if we can start copying some data to the user */
|
||||
if (length > *skip_bytes) {
|
||||
length -= *skip_bytes;
|
||||
if (length > *nbytes)
|
||||
length = *nbytes;
|
||||
if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) {
|
||||
free_pages((unsigned long)pages_start, 1);
|
||||
return -EFAULT;
|
||||
}
|
||||
*nbytes -= length;
|
||||
*file_offset += length;
|
||||
total_written += length;
|
||||
*buffer += length;
|
||||
*skip_bytes = 0;
|
||||
} else
|
||||
*skip_bytes -= length;
|
||||
|
||||
free_pages((unsigned long)pages_start, 1);
|
||||
|
||||
/* Now look at all of this device's children. */
|
||||
usb_hub_for_each_child(usbdev, chix, childdev) {
|
||||
usb_lock_device(childdev);
|
||||
ret = usb_device_dump(buffer, nbytes, skip_bytes,
|
||||
file_offset, childdev, bus,
|
||||
level + 1, chix - 1, ++cnt);
|
||||
usb_unlock_device(childdev);
|
||||
if (ret == -EFAULT)
|
||||
return total_written;
|
||||
total_written += ret;
|
||||
}
|
||||
return total_written;
|
||||
}
|
||||
|
||||
static ssize_t usb_device_read(struct file *file, char __user *buf,
|
||||
size_t nbytes, loff_t *ppos)
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
ssize_t ret, total_written = 0;
|
||||
loff_t skip_bytes = *ppos;
|
||||
|
||||
if (*ppos < 0)
|
||||
return -EINVAL;
|
||||
if (nbytes <= 0)
|
||||
return 0;
|
||||
if (!access_ok(VERIFY_WRITE, buf, nbytes))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
/* print devices for all busses */
|
||||
list_for_each_entry(bus, &usb_bus_list, bus_list) {
|
||||
/* recurse through all children of the root hub */
|
||||
if (!bus_to_hcd(bus)->rh_registered)
|
||||
continue;
|
||||
usb_lock_device(bus->root_hub);
|
||||
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
|
||||
bus->root_hub, bus, 0, 0, 0);
|
||||
usb_unlock_device(bus->root_hub);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
return ret;
|
||||
}
|
||||
total_written += ret;
|
||||
}
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
return total_written;
|
||||
}
|
||||
|
||||
/* Kernel lock for "lastev" protection */
|
||||
static unsigned int usb_device_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
unsigned int event_count;
|
||||
|
||||
poll_wait(file, &device_event.wait, wait);
|
||||
|
||||
event_count = atomic_read(&device_event.count);
|
||||
if (file->f_version != event_count) {
|
||||
file->f_version = event_count;
|
||||
return POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static loff_t usb_device_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
loff_t ret;
|
||||
|
||||
mutex_lock(&file_inode(file)->i_mutex);
|
||||
|
||||
switch (orig) {
|
||||
case 0:
|
||||
file->f_pos = offset;
|
||||
ret = file->f_pos;
|
||||
break;
|
||||
case 1:
|
||||
file->f_pos += offset;
|
||||
ret = file->f_pos;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&file_inode(file)->i_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct file_operations usbfs_devices_fops = {
|
||||
.llseek = usb_device_lseek,
|
||||
.read = usb_device_read,
|
||||
.poll = usb_device_poll,
|
||||
};
|
2523
drivers/usb/core/devio.c
Normal file
2523
drivers/usb/core/devio.c
Normal file
File diff suppressed because it is too large
Load diff
1880
drivers/usb/core/driver.c
Normal file
1880
drivers/usb/core/driver.c
Normal file
File diff suppressed because it is too large
Load diff
217
drivers/usb/core/endpoint.c
Normal file
217
drivers/usb/core/endpoint.c
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* drivers/usb/core/endpoint.c
|
||||
*
|
||||
* (C) Copyright 2002,2004,2006 Greg Kroah-Hartman
|
||||
* (C) Copyright 2002,2004 IBM Corp.
|
||||
* (C) Copyright 2006 Novell Inc.
|
||||
*
|
||||
* Endpoint sysfs stuff
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
#include "usb.h"
|
||||
|
||||
struct ep_device {
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
struct usb_device *udev;
|
||||
struct device dev;
|
||||
};
|
||||
#define to_ep_device(_dev) \
|
||||
container_of(_dev, struct ep_device, dev)
|
||||
|
||||
struct ep_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct usb_device *,
|
||||
struct usb_endpoint_descriptor *, char *);
|
||||
};
|
||||
#define to_ep_attribute(_attr) \
|
||||
container_of(_attr, struct ep_attribute, attr)
|
||||
|
||||
#define usb_ep_attr(field, format_string) \
|
||||
static ssize_t field##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct ep_device *ep = to_ep_device(dev); \
|
||||
return sprintf(buf, format_string, ep->desc->field); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(field)
|
||||
|
||||
usb_ep_attr(bLength, "%02x\n");
|
||||
usb_ep_attr(bEndpointAddress, "%02x\n");
|
||||
usb_ep_attr(bmAttributes, "%02x\n");
|
||||
usb_ep_attr(bInterval, "%02x\n");
|
||||
|
||||
static ssize_t wMaxPacketSize_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ep_device *ep = to_ep_device(dev);
|
||||
return sprintf(buf, "%04x\n",
|
||||
usb_endpoint_maxp(ep->desc) & 0x07ff);
|
||||
}
|
||||
static DEVICE_ATTR_RO(wMaxPacketSize);
|
||||
|
||||
static ssize_t type_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ep_device *ep = to_ep_device(dev);
|
||||
char *type = "unknown";
|
||||
|
||||
switch (usb_endpoint_type(ep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
type = "Control";
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
type = "Isoc";
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
type = "Bulk";
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
type = "Interrupt";
|
||||
break;
|
||||
}
|
||||
return sprintf(buf, "%s\n", type);
|
||||
}
|
||||
static DEVICE_ATTR_RO(type);
|
||||
|
||||
static ssize_t interval_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ep_device *ep = to_ep_device(dev);
|
||||
char unit;
|
||||
unsigned interval = 0;
|
||||
unsigned in;
|
||||
|
||||
in = (ep->desc->bEndpointAddress & USB_DIR_IN);
|
||||
|
||||
switch (usb_endpoint_type(ep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
if (ep->udev->speed == USB_SPEED_HIGH)
|
||||
/* uframes per NAK */
|
||||
interval = ep->desc->bInterval;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
interval = 1 << (ep->desc->bInterval - 1);
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (ep->udev->speed == USB_SPEED_HIGH && !in)
|
||||
/* uframes per NAK */
|
||||
interval = ep->desc->bInterval;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
if (ep->udev->speed == USB_SPEED_HIGH)
|
||||
interval = 1 << (ep->desc->bInterval - 1);
|
||||
else
|
||||
interval = ep->desc->bInterval;
|
||||
break;
|
||||
}
|
||||
interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
|
||||
if (interval % 1000)
|
||||
unit = 'u';
|
||||
else {
|
||||
unit = 'm';
|
||||
interval /= 1000;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d%cs\n", interval, unit);
|
||||
}
|
||||
static DEVICE_ATTR_RO(interval);
|
||||
|
||||
static ssize_t direction_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ep_device *ep = to_ep_device(dev);
|
||||
char *direction;
|
||||
|
||||
if (usb_endpoint_xfer_control(ep->desc))
|
||||
direction = "both";
|
||||
else if (usb_endpoint_dir_in(ep->desc))
|
||||
direction = "in";
|
||||
else
|
||||
direction = "out";
|
||||
return sprintf(buf, "%s\n", direction);
|
||||
}
|
||||
static DEVICE_ATTR_RO(direction);
|
||||
|
||||
static struct attribute *ep_dev_attrs[] = {
|
||||
&dev_attr_bLength.attr,
|
||||
&dev_attr_bEndpointAddress.attr,
|
||||
&dev_attr_bmAttributes.attr,
|
||||
&dev_attr_bInterval.attr,
|
||||
&dev_attr_wMaxPacketSize.attr,
|
||||
&dev_attr_interval.attr,
|
||||
&dev_attr_type.attr,
|
||||
&dev_attr_direction.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group ep_dev_attr_grp = {
|
||||
.attrs = ep_dev_attrs,
|
||||
};
|
||||
static const struct attribute_group *ep_dev_groups[] = {
|
||||
&ep_dev_attr_grp,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void ep_device_release(struct device *dev)
|
||||
{
|
||||
struct ep_device *ep_dev = to_ep_device(dev);
|
||||
|
||||
kfree(ep_dev);
|
||||
}
|
||||
|
||||
struct device_type usb_ep_device_type = {
|
||||
.name = "usb_endpoint",
|
||||
.release = ep_device_release,
|
||||
};
|
||||
|
||||
int usb_create_ep_devs(struct device *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
struct ep_device *ep_dev;
|
||||
int retval;
|
||||
|
||||
ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
|
||||
if (!ep_dev) {
|
||||
retval = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ep_dev->desc = &endpoint->desc;
|
||||
ep_dev->udev = udev;
|
||||
ep_dev->dev.groups = ep_dev_groups;
|
||||
ep_dev->dev.type = &usb_ep_device_type;
|
||||
ep_dev->dev.parent = parent;
|
||||
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
|
||||
retval = device_register(&ep_dev->dev);
|
||||
if (retval)
|
||||
goto error_register;
|
||||
|
||||
device_enable_async_suspend(&ep_dev->dev);
|
||||
endpoint->ep_dev = ep_dev;
|
||||
return retval;
|
||||
|
||||
error_register:
|
||||
put_device(&ep_dev->dev);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
|
||||
{
|
||||
struct ep_device *ep_dev = endpoint->ep_dev;
|
||||
|
||||
if (ep_dev) {
|
||||
device_unregister(&ep_dev->dev);
|
||||
endpoint->ep_dev = NULL;
|
||||
}
|
||||
}
|
245
drivers/usb/core/file.c
Normal file
245
drivers/usb/core/file.c
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* drivers/usb/core/file.c
|
||||
*
|
||||
* (C) Copyright Linus Torvalds 1999
|
||||
* (C) Copyright Johannes Erdfelt 1999-2001
|
||||
* (C) Copyright Andreas Gal 1999
|
||||
* (C) Copyright Gregory P. Smith 1999
|
||||
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
|
||||
* (C) Copyright Randy Dunlap 2000
|
||||
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
|
||||
* more docs, etc)
|
||||
* (C) Copyright Yggdrasil Computing, Inc. 2000
|
||||
* (usb_device_id matching changes by Adam J. Richter)
|
||||
* (C) Copyright Greg Kroah-Hartman 2002-2003
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
#define MAX_USB_MINORS 256
|
||||
static const struct file_operations *usb_minors[MAX_USB_MINORS];
|
||||
static DECLARE_RWSEM(minor_rwsem);
|
||||
|
||||
static int usb_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
const struct file_operations *new_fops;
|
||||
|
||||
down_read(&minor_rwsem);
|
||||
new_fops = fops_get(usb_minors[iminor(inode)]);
|
||||
|
||||
if (!new_fops)
|
||||
goto done;
|
||||
|
||||
replace_fops(file, new_fops);
|
||||
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
|
||||
if (file->f_op->open)
|
||||
err = file->f_op->open(inode, file);
|
||||
done:
|
||||
up_read(&minor_rwsem);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct file_operations usb_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = usb_open,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static struct usb_class {
|
||||
struct kref kref;
|
||||
struct class *class;
|
||||
} *usb_class;
|
||||
|
||||
static char *usb_devnode(struct device *dev, umode_t *mode)
|
||||
{
|
||||
struct usb_class_driver *drv;
|
||||
|
||||
drv = dev_get_drvdata(dev);
|
||||
if (!drv || !drv->devnode)
|
||||
return NULL;
|
||||
return drv->devnode(dev, mode);
|
||||
}
|
||||
|
||||
static int init_usb_class(void)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (usb_class != NULL) {
|
||||
kref_get(&usb_class->kref);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL);
|
||||
if (!usb_class) {
|
||||
result = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
kref_init(&usb_class->kref);
|
||||
usb_class->class = class_create(THIS_MODULE, "usbmisc");
|
||||
if (IS_ERR(usb_class->class)) {
|
||||
result = PTR_ERR(usb_class->class);
|
||||
printk(KERN_ERR "class_create failed for usb devices\n");
|
||||
kfree(usb_class);
|
||||
usb_class = NULL;
|
||||
goto exit;
|
||||
}
|
||||
usb_class->class->devnode = usb_devnode;
|
||||
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void release_usb_class(struct kref *kref)
|
||||
{
|
||||
/* Ok, we cheat as we know we only have one usb_class */
|
||||
class_destroy(usb_class->class);
|
||||
kfree(usb_class);
|
||||
usb_class = NULL;
|
||||
}
|
||||
|
||||
static void destroy_usb_class(void)
|
||||
{
|
||||
if (usb_class)
|
||||
kref_put(&usb_class->kref, release_usb_class);
|
||||
}
|
||||
|
||||
int usb_major_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
|
||||
if (error)
|
||||
printk(KERN_ERR "Unable to get major %d for usb devices\n",
|
||||
USB_MAJOR);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void usb_major_cleanup(void)
|
||||
{
|
||||
unregister_chrdev(USB_MAJOR, "usb");
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_register_dev - register a USB device, and ask for a minor number
|
||||
* @intf: pointer to the usb_interface that is being registered
|
||||
* @class_driver: pointer to the usb_class_driver for this device
|
||||
*
|
||||
* This should be called by all USB drivers that use the USB major number.
|
||||
* If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be
|
||||
* dynamically allocated out of the list of available ones. If it is not
|
||||
* enabled, the minor number will be based on the next available free minor,
|
||||
* starting at the class_driver->minor_base.
|
||||
*
|
||||
* This function also creates a usb class device in the sysfs tree.
|
||||
*
|
||||
* usb_deregister_dev() must be called when the driver is done with
|
||||
* the minor numbers given out by this function.
|
||||
*
|
||||
* Return: -EINVAL if something bad happens with trying to register a
|
||||
* device, and 0 on success.
|
||||
*/
|
||||
int usb_register_dev(struct usb_interface *intf,
|
||||
struct usb_class_driver *class_driver)
|
||||
{
|
||||
int retval;
|
||||
int minor_base = class_driver->minor_base;
|
||||
int minor;
|
||||
char name[20];
|
||||
char *temp;
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
/*
|
||||
* We don't care what the device tries to start at, we want to start
|
||||
* at zero to pack the devices into the smallest available space with
|
||||
* no holes in the minor range.
|
||||
*/
|
||||
minor_base = 0;
|
||||
#endif
|
||||
|
||||
if (class_driver->fops == NULL)
|
||||
return -EINVAL;
|
||||
if (intf->minor >= 0)
|
||||
return -EADDRINUSE;
|
||||
|
||||
retval = init_usb_class();
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);
|
||||
|
||||
down_write(&minor_rwsem);
|
||||
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
|
||||
if (usb_minors[minor])
|
||||
continue;
|
||||
|
||||
usb_minors[minor] = class_driver->fops;
|
||||
intf->minor = minor;
|
||||
break;
|
||||
}
|
||||
up_write(&minor_rwsem);
|
||||
if (intf->minor < 0)
|
||||
return -EXFULL;
|
||||
|
||||
/* 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);
|
||||
if (IS_ERR(intf->usb_dev)) {
|
||||
down_write(&minor_rwsem);
|
||||
usb_minors[minor] = NULL;
|
||||
intf->minor = -1;
|
||||
up_write(&minor_rwsem);
|
||||
retval = PTR_ERR(intf->usb_dev);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_register_dev);
|
||||
|
||||
/**
|
||||
* usb_deregister_dev - deregister a USB device's dynamic minor.
|
||||
* @intf: pointer to the usb_interface that is being deregistered
|
||||
* @class_driver: pointer to the usb_class_driver for this device
|
||||
*
|
||||
* Used in conjunction with usb_register_dev(). This function is called
|
||||
* when the USB driver is finished with the minor numbers gotten from a
|
||||
* call to usb_register_dev() (usually when the device is disconnected
|
||||
* from the system.)
|
||||
*
|
||||
* This function also removes the usb class device from the sysfs tree.
|
||||
*
|
||||
* This should be called by all drivers that use the USB major number.
|
||||
*/
|
||||
void usb_deregister_dev(struct usb_interface *intf,
|
||||
struct usb_class_driver *class_driver)
|
||||
{
|
||||
if (intf->minor == -1)
|
||||
return;
|
||||
|
||||
dev_dbg(&intf->dev, "removing %d minor\n", intf->minor);
|
||||
|
||||
down_write(&minor_rwsem);
|
||||
usb_minors[intf->minor] = NULL;
|
||||
up_write(&minor_rwsem);
|
||||
|
||||
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
|
||||
intf->usb_dev = NULL;
|
||||
intf->minor = -1;
|
||||
destroy_usb_class();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_deregister_dev);
|
247
drivers/usb/core/generic.c
Normal file
247
drivers/usb/core/generic.c
Normal file
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* drivers/usb/generic.c - generic driver for USB devices (not interfaces)
|
||||
*
|
||||
* (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
|
||||
*
|
||||
* based on drivers/usb/usb.c which had the following copyrights:
|
||||
* (C) Copyright Linus Torvalds 1999
|
||||
* (C) Copyright Johannes Erdfelt 1999-2001
|
||||
* (C) Copyright Andreas Gal 1999
|
||||
* (C) Copyright Gregory P. Smith 1999
|
||||
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
|
||||
* (C) Copyright Randy Dunlap 2000
|
||||
* (C) Copyright David Brownell 2000-2004
|
||||
* (C) Copyright Yggdrasil Computing, Inc. 2000
|
||||
* (usb_device_id matching changes by Adam J. Richter)
|
||||
* (C) Copyright Greg Kroah-Hartman 2002-2003
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "usb.h"
|
||||
|
||||
static inline const char *plural(int n)
|
||||
{
|
||||
return (n == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
static int is_rndis(struct usb_interface_descriptor *desc)
|
||||
{
|
||||
return desc->bInterfaceClass == USB_CLASS_COMM
|
||||
&& desc->bInterfaceSubClass == 2
|
||||
&& desc->bInterfaceProtocol == 0xff;
|
||||
}
|
||||
|
||||
static int is_activesync(struct usb_interface_descriptor *desc)
|
||||
{
|
||||
return desc->bInterfaceClass == USB_CLASS_MISC
|
||||
&& desc->bInterfaceSubClass == 1
|
||||
&& desc->bInterfaceProtocol == 1;
|
||||
}
|
||||
|
||||
int usb_choose_configuration(struct usb_device *udev)
|
||||
{
|
||||
int i;
|
||||
int num_configs;
|
||||
int insufficient_power = 0;
|
||||
struct usb_host_config *c, *best;
|
||||
|
||||
if (usb_device_is_owned(udev))
|
||||
return 0;
|
||||
|
||||
best = NULL;
|
||||
c = udev->config;
|
||||
num_configs = udev->descriptor.bNumConfigurations;
|
||||
for (i = 0; i < num_configs; (i++, c++)) {
|
||||
struct usb_interface_descriptor *desc = NULL;
|
||||
|
||||
/* It's possible that a config has no interfaces! */
|
||||
if (c->desc.bNumInterfaces > 0)
|
||||
desc = &c->intf_cache[0]->altsetting->desc;
|
||||
|
||||
/*
|
||||
* HP's USB bus-powered keyboard has only one configuration
|
||||
* and it claims to be self-powered; other devices may have
|
||||
* similar errors in their descriptors. If the next test
|
||||
* were allowed to execute, such configurations would always
|
||||
* be rejected and the devices would not work as expected.
|
||||
* In the meantime, we run the risk of selecting a config
|
||||
* that requires external power at a time when that power
|
||||
* isn't available. It seems to be the lesser of two evils.
|
||||
*
|
||||
* Bugzilla #6448 reports a device that appears to crash
|
||||
* when it receives a GET_DEVICE_STATUS request! We don't
|
||||
* have any other way to tell whether a device is self-powered,
|
||||
* but since we don't use that information anywhere but here,
|
||||
* the call has been removed.
|
||||
*
|
||||
* Maybe the GET_DEVICE_STATUS call and the test below can
|
||||
* be reinstated when device firmwares become more reliable.
|
||||
* Don't hold your breath.
|
||||
*/
|
||||
#if 0
|
||||
/* Rule out self-powered configs for a bus-powered device */
|
||||
if (bus_powered && (c->desc.bmAttributes &
|
||||
USB_CONFIG_ATT_SELFPOWER))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The next test may not be as effective as it should be.
|
||||
* Some hubs have errors in their descriptor, claiming
|
||||
* to be self-powered when they are really bus-powered.
|
||||
* We will overestimate the amount of current such hubs
|
||||
* make available for each port.
|
||||
*
|
||||
* This is a fairly benign sort of failure. It won't
|
||||
* cause us to reject configurations that we should have
|
||||
* accepted.
|
||||
*/
|
||||
|
||||
/* Rule out configs that draw too much bus current */
|
||||
if (usb_get_max_power(udev, c) > udev->bus_mA) {
|
||||
insufficient_power++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* When the first config's first interface is one of Microsoft's
|
||||
* pet nonstandard Ethernet-over-USB protocols, ignore it unless
|
||||
* this kernel has enabled the necessary host side driver.
|
||||
* But: Don't ignore it if it's the only config.
|
||||
*/
|
||||
if (i == 0 && num_configs > 1 && desc &&
|
||||
(is_rndis(desc) || is_activesync(desc))) {
|
||||
#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
|
||||
continue;
|
||||
#else
|
||||
best = c;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* From the remaining configs, choose the first one whose
|
||||
* first interface is for a non-vendor-specific class.
|
||||
* Reason: Linux is more likely to have a class driver
|
||||
* than a vendor-specific driver. */
|
||||
else if (udev->descriptor.bDeviceClass !=
|
||||
USB_CLASS_VENDOR_SPEC &&
|
||||
(desc && desc->bInterfaceClass !=
|
||||
USB_CLASS_VENDOR_SPEC)) {
|
||||
best = c;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If all the remaining configs are vendor-specific,
|
||||
* choose the first one. */
|
||||
else if (!best)
|
||||
best = c;
|
||||
}
|
||||
|
||||
if (insufficient_power > 0)
|
||||
dev_info(&udev->dev, "rejected %d configuration%s "
|
||||
"due to insufficient available bus power\n",
|
||||
insufficient_power, plural(insufficient_power));
|
||||
|
||||
if (best) {
|
||||
i = best->desc.bConfigurationValue;
|
||||
dev_dbg(&udev->dev,
|
||||
"configuration #%d chosen from %d choice%s\n",
|
||||
i, num_configs, plural(num_configs));
|
||||
} else {
|
||||
i = -1;
|
||||
dev_warn(&udev->dev,
|
||||
"no configuration chosen from %d choice%s\n",
|
||||
num_configs, plural(num_configs));
|
||||
}
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_choose_configuration);
|
||||
|
||||
static int generic_probe(struct usb_device *udev)
|
||||
{
|
||||
int err, c;
|
||||
|
||||
/* Choose and set the configuration. This registers the interfaces
|
||||
* with the driver core and lets interface drivers bind to them.
|
||||
*/
|
||||
if (udev->authorized == 0)
|
||||
dev_err(&udev->dev, "Device is not authorized for usage\n");
|
||||
else {
|
||||
c = usb_choose_configuration(udev);
|
||||
if (c >= 0) {
|
||||
err = usb_set_configuration(udev, c);
|
||||
if (err && err != -ENODEV) {
|
||||
dev_err(&udev->dev, "can't set config #%d, error %d\n",
|
||||
c, err);
|
||||
/* This need not be fatal. The user can try to
|
||||
* set other configurations. */
|
||||
}
|
||||
}
|
||||
}
|
||||
/* USB device state == configured ... usable */
|
||||
usb_notify_add_device(udev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void generic_disconnect(struct usb_device *udev)
|
||||
{
|
||||
usb_notify_remove_device(udev);
|
||||
|
||||
/* if this is only an unbind, not a physical disconnect, then
|
||||
* unconfigure the device */
|
||||
if (udev->actconfig)
|
||||
usb_set_configuration(udev, -1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int generic_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Normal USB devices suspend through their upstream port.
|
||||
* Root hubs don't have upstream ports to suspend,
|
||||
* so we have to shut down their downstream HC-to-USB
|
||||
* interfaces manually by doing a bus (or "global") suspend.
|
||||
*/
|
||||
if (!udev->parent)
|
||||
rc = hcd_bus_suspend(udev, msg);
|
||||
|
||||
/* Non-root devices don't need to do anything for FREEZE or PRETHAW */
|
||||
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
|
||||
rc = 0;
|
||||
else
|
||||
rc = usb_port_suspend(udev, msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int generic_resume(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Normal USB devices resume/reset through their upstream port.
|
||||
* Root hubs don't have upstream ports to resume or reset,
|
||||
* so we have to start up their downstream HC-to-USB
|
||||
* interfaces manually by doing a bus (or "global") resume.
|
||||
*/
|
||||
if (!udev->parent)
|
||||
rc = hcd_bus_resume(udev, msg);
|
||||
else
|
||||
rc = usb_port_resume(udev, msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
struct usb_device_driver usb_generic_driver = {
|
||||
.name = "usb",
|
||||
.probe = generic_probe,
|
||||
.disconnect = generic_disconnect,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = generic_suspend,
|
||||
.resume = generic_resume,
|
||||
#endif
|
||||
.supports_autosuspend = 1,
|
||||
};
|
658
drivers/usb/core/hcd-pci.c
Normal file
658
drivers/usb/core/hcd-pci.c
Normal file
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
* (C) Copyright David Brownell 2000-2002
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pmac_feature.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/prom.h>
|
||||
#endif
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
|
||||
/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
|
||||
|
||||
/*
|
||||
* Coordinate handoffs between EHCI and companion controllers
|
||||
* during EHCI probing and system resume.
|
||||
*/
|
||||
|
||||
static DECLARE_RWSEM(companions_rwsem);
|
||||
|
||||
#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI
|
||||
#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI
|
||||
#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI
|
||||
|
||||
static inline int is_ohci_or_uhci(struct pci_dev *pdev)
|
||||
{
|
||||
return pdev->class == CL_OHCI || pdev->class == CL_UHCI;
|
||||
}
|
||||
|
||||
typedef void (*companion_fn)(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd);
|
||||
|
||||
/* Iterate over PCI devices in the same slot as pdev and call fn for each */
|
||||
static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
companion_fn fn)
|
||||
{
|
||||
struct pci_dev *companion;
|
||||
struct usb_hcd *companion_hcd;
|
||||
unsigned int slot = PCI_SLOT(pdev->devfn);
|
||||
|
||||
/*
|
||||
* Iterate through other PCI functions in the same slot.
|
||||
* If the function's drvdata isn't set then it isn't bound to
|
||||
* a USB host controller driver, so skip it.
|
||||
*/
|
||||
companion = NULL;
|
||||
for_each_pci_dev(companion) {
|
||||
if (companion->bus != pdev->bus ||
|
||||
PCI_SLOT(companion->devfn) != slot)
|
||||
continue;
|
||||
companion_hcd = pci_get_drvdata(companion);
|
||||
if (!companion_hcd || !companion_hcd->self.root_hub)
|
||||
continue;
|
||||
fn(pdev, hcd, companion, companion_hcd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We're about to add an EHCI controller, which will unceremoniously grab
|
||||
* all the port connections away from its companions. To prevent annoying
|
||||
* error messages, lock the companion's root hub and gracefully unconfigure
|
||||
* it beforehand. Leave it locked until the EHCI controller is all set.
|
||||
*/
|
||||
static void ehci_pre_add(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
if (is_ohci_or_uhci(companion)) {
|
||||
udev = companion_hcd->self.root_hub;
|
||||
usb_lock_device(udev);
|
||||
usb_set_configuration(udev, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adding the EHCI controller has either succeeded or failed. Set the
|
||||
* companion pointer accordingly, and in either case, reconfigure and
|
||||
* unlock the root hub.
|
||||
*/
|
||||
static void ehci_post_add(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
if (is_ohci_or_uhci(companion)) {
|
||||
if (dev_get_drvdata(&pdev->dev)) { /* Succeeded */
|
||||
dev_dbg(&pdev->dev, "HS companion for %s\n",
|
||||
dev_name(&companion->dev));
|
||||
companion_hcd->self.hs_companion = &hcd->self;
|
||||
}
|
||||
udev = companion_hcd->self.root_hub;
|
||||
usb_set_configuration(udev, 1);
|
||||
usb_unlock_device(udev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We just added a non-EHCI controller. Find the EHCI controller to
|
||||
* which it is a companion, and store a pointer to the bus structure.
|
||||
*/
|
||||
static void non_ehci_add(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
if (is_ohci_or_uhci(pdev) && companion->class == CL_EHCI) {
|
||||
dev_dbg(&pdev->dev, "FS/LS companion for %s\n",
|
||||
dev_name(&companion->dev));
|
||||
hcd->self.hs_companion = &companion_hcd->self;
|
||||
}
|
||||
}
|
||||
|
||||
/* We are removing an EHCI controller. Clear the companions' pointers. */
|
||||
static void ehci_remove(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
if (is_ohci_or_uhci(companion))
|
||||
companion_hcd->self.hs_companion = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* An EHCI controller must wait for its companions before resuming. */
|
||||
static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
if (is_ohci_or_uhci(companion))
|
||||
device_pm_wait_for_dev(&pdev->dev, &companion->dev);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* configure so an HC device and id are always provided */
|
||||
/* always called with process context; sleeping is OK */
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_probe - initialize PCI-based HCDs
|
||||
* @dev: USB Host Controller being probed
|
||||
* @id: pci hotplug id connecting controller to HCD framework
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Allocates basic PCI resources for this USB host controller, and
|
||||
* then invokes the start() method for the HCD associated with it
|
||||
* through the hotplug entry's driver_data.
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as probe().
|
||||
*
|
||||
* Return: 0 if successful.
|
||||
*/
|
||||
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct hc_driver *driver;
|
||||
struct usb_hcd *hcd;
|
||||
int retval;
|
||||
int hcd_irq = 0;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
if (!id)
|
||||
return -EINVAL;
|
||||
driver = (struct hc_driver *)id->driver_data;
|
||||
if (!driver)
|
||||
return -EINVAL;
|
||||
|
||||
if (pci_enable_device(dev) < 0)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* 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 (!dev->irq) {
|
||||
dev_err(&dev->dev,
|
||||
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
|
||||
pci_name(dev));
|
||||
retval = -ENODEV;
|
||||
goto disable_pci;
|
||||
}
|
||||
hcd_irq = dev->irq;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto disable_pci;
|
||||
}
|
||||
|
||||
hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
|
||||
driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
|
||||
|
||||
if (driver->flags & HCD_MEMORY) {
|
||||
/* EHCI, OHCI */
|
||||
hcd->rsrc_start = pci_resource_start(dev, 0);
|
||||
hcd->rsrc_len = pci_resource_len(dev, 0);
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
dev_dbg(&dev->dev, "controller already in use\n");
|
||||
retval = -EBUSY;
|
||||
goto put_hcd;
|
||||
}
|
||||
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&dev->dev, "error mapping memory\n");
|
||||
retval = -EFAULT;
|
||||
goto release_mem_region;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* UHCI */
|
||||
int region;
|
||||
|
||||
for (region = 0; region < PCI_ROM_RESOURCE; region++) {
|
||||
if (!(pci_resource_flags(dev, region) &
|
||||
IORESOURCE_IO))
|
||||
continue;
|
||||
|
||||
hcd->rsrc_start = pci_resource_start(dev, region);
|
||||
hcd->rsrc_len = pci_resource_len(dev, region);
|
||||
if (request_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description))
|
||||
break;
|
||||
}
|
||||
if (region == PCI_ROM_RESOURCE) {
|
||||
dev_dbg(&dev->dev, "no i/o regions available\n");
|
||||
retval = -EBUSY;
|
||||
goto put_hcd;
|
||||
}
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
/* Note: dev_set_drvdata must be called while holding the rwsem */
|
||||
if (dev->class == CL_EHCI) {
|
||||
down_write(&companions_rwsem);
|
||||
dev_set_drvdata(&dev->dev, hcd);
|
||||
for_each_companion(dev, hcd, ehci_pre_add);
|
||||
retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
for_each_companion(dev, hcd, ehci_post_add);
|
||||
up_write(&companions_rwsem);
|
||||
} else {
|
||||
down_read(&companions_rwsem);
|
||||
dev_set_drvdata(&dev->dev, hcd);
|
||||
retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
else
|
||||
for_each_companion(dev, hcd, non_ehci_add);
|
||||
up_read(&companions_rwsem);
|
||||
}
|
||||
|
||||
if (retval != 0)
|
||||
goto unmap_registers;
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
|
||||
if (pci_dev_run_wake(dev))
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
return retval;
|
||||
|
||||
unmap_registers:
|
||||
if (driver->flags & HCD_MEMORY) {
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
} else
|
||||
release_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
disable_pci:
|
||||
pci_disable_device(dev);
|
||||
dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_probe);
|
||||
|
||||
|
||||
/* may be called without controller electrically present */
|
||||
/* may be called with controller, bus, and devices active */
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
|
||||
* @dev: USB Host Controller being removed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Reverses the effect of usb_hcd_pci_probe(), first invoking
|
||||
* the HCD's stop() method. It is always called from a thread
|
||||
* context, normally "rmmod", "apmd", or something similar.
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as remove().
|
||||
*/
|
||||
void usb_hcd_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
if (!hcd)
|
||||
return;
|
||||
|
||||
if (pci_dev_run_wake(dev))
|
||||
pm_runtime_get_noresume(&dev->dev);
|
||||
|
||||
/* Fake an interrupt request in order to give the driver a chance
|
||||
* to test whether the controller hardware has been removed (e.g.,
|
||||
* cardbus physical eject).
|
||||
*/
|
||||
local_irq_disable();
|
||||
usb_hcd_irq(0, hcd);
|
||||
local_irq_enable();
|
||||
|
||||
/* Note: dev_set_drvdata must be called while holding the rwsem */
|
||||
if (dev->class == CL_EHCI) {
|
||||
down_write(&companions_rwsem);
|
||||
for_each_companion(dev, hcd, ehci_remove);
|
||||
usb_remove_hcd(hcd);
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
up_write(&companions_rwsem);
|
||||
} else {
|
||||
/* Not EHCI; just clear the companion pointer */
|
||||
down_read(&companions_rwsem);
|
||||
hcd->self.hs_companion = NULL;
|
||||
usb_remove_hcd(hcd);
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
up_read(&companions_rwsem);
|
||||
}
|
||||
|
||||
if (hcd->driver->flags & HCD_MEMORY) {
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
} else {
|
||||
release_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
}
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_shutdown - shutdown host controller
|
||||
* @dev: USB Host Controller being shutdown
|
||||
*/
|
||||
void usb_hcd_pci_shutdown(struct pci_dev *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
if (!hcd)
|
||||
return;
|
||||
|
||||
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
|
||||
hcd->driver->shutdown) {
|
||||
hcd->driver->shutdown(hcd);
|
||||
if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
|
||||
free_irq(hcd->irq, hcd);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
static void powermac_set_asic(struct pci_dev *pci_dev, int enable)
|
||||
{
|
||||
/* Enanble or disable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node(pci_dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, enable);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_PPC_PMAC */
|
||||
|
||||
static int check_root_hub_suspended(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
|
||||
if (HCD_RH_RUNNING(hcd)) {
|
||||
dev_warn(dev, "Root hub is not suspended\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (hcd->shared_hcd) {
|
||||
hcd = hcd->shared_hcd;
|
||||
if (HCD_RH_RUNNING(hcd)) {
|
||||
dev_warn(dev, "Secondary root hub is not suspended\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
|
||||
static int suspend_common(struct device *dev, bool do_wakeup)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
/* Root hub suspend should have stopped all downstream traffic,
|
||||
* and all bus master traffic. And done so for both the interface
|
||||
* and the stub usb_device (which we check here). But maybe it
|
||||
* didn't; writing sysfs power/state files ignores such rules...
|
||||
*/
|
||||
retval = check_root_hub_suspended(dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) {
|
||||
/* Optimization: Don't suspend if a root-hub wakeup is
|
||||
* pending and it would cause the HCD to wake up anyway.
|
||||
*/
|
||||
if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
|
||||
return -EBUSY;
|
||||
if (do_wakeup && hcd->shared_hcd &&
|
||||
HCD_WAKEUP_PENDING(hcd->shared_hcd))
|
||||
return -EBUSY;
|
||||
retval = hcd->driver->pci_suspend(hcd, do_wakeup);
|
||||
suspend_report_result(hcd->driver->pci_suspend, retval);
|
||||
|
||||
/* Check again in case wakeup raced with pci_suspend */
|
||||
if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) ||
|
||||
(retval == 0 && do_wakeup && hcd->shared_hcd &&
|
||||
HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
|
||||
if (hcd->driver->pci_resume)
|
||||
hcd->driver->pci_resume(hcd, false);
|
||||
retval = -EBUSY;
|
||||
}
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* If MSI-X is enabled, the driver will have synchronized all vectors
|
||||
* in pci_suspend(). If MSI or legacy PCI is enabled, that will be
|
||||
* synchronized here.
|
||||
*/
|
||||
if (!hcd->msix_enabled)
|
||||
synchronize_irq(pci_dev->irq);
|
||||
|
||||
/* Downstream ports from this root hub should already be quiesced, so
|
||||
* there will be no DMA activity. Now we can shut down the upstream
|
||||
* link (except maybe for PME# resume signaling). We'll enter a
|
||||
* low power state during suspend_noirq, if the hardware allows.
|
||||
*/
|
||||
pci_disable_device(pci_dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int resume_common(struct device *dev, int event)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
if (HCD_RH_RUNNING(hcd) ||
|
||||
(hcd->shared_hcd &&
|
||||
HCD_RH_RUNNING(hcd->shared_hcd))) {
|
||||
dev_dbg(dev, "can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
retval = pci_enable_device(pci_dev);
|
||||
if (retval < 0) {
|
||||
dev_err(dev, "can't re-enable after resume, %d!\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) {
|
||||
|
||||
/*
|
||||
* Only EHCI controllers have to wait for their companions.
|
||||
* No locking is needed because PCI controller drivers do not
|
||||
* get unbound during system resume.
|
||||
*/
|
||||
if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME)
|
||||
for_each_companion(pci_dev, hcd,
|
||||
ehci_wait_for_companions);
|
||||
|
||||
retval = hcd->driver->pci_resume(hcd,
|
||||
event == PM_EVENT_RESTORE);
|
||||
if (retval) {
|
||||
dev_err(dev, "PCI post-resume error %d!\n", retval);
|
||||
if (hcd->shared_hcd)
|
||||
usb_hc_died(hcd->shared_hcd);
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
#endif /* SLEEP || RUNTIME */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int hcd_pci_suspend(struct device *dev)
|
||||
{
|
||||
return suspend_common(dev, device_may_wakeup(dev));
|
||||
}
|
||||
|
||||
static int hcd_pci_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
|
||||
int retval;
|
||||
|
||||
retval = check_root_hub_suspended(dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
pci_save_state(pci_dev);
|
||||
|
||||
/* If the root hub is dead rather than suspended, disallow remote
|
||||
* wakeup. usb_hc_died() should ensure that both hosts are marked as
|
||||
* dying, so we only need to check the primary roothub.
|
||||
*/
|
||||
if (HCD_DEAD(hcd))
|
||||
device_set_wakeup_enable(dev, 0);
|
||||
dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev));
|
||||
|
||||
/* Possibly enable remote wakeup,
|
||||
* choose the appropriate low-power state, and go to that state.
|
||||
*/
|
||||
retval = pci_prepare_to_sleep(pci_dev);
|
||||
if (retval == -EIO) { /* Low-power not supported */
|
||||
dev_dbg(dev, "--> PCI D0 legacy\n");
|
||||
retval = 0;
|
||||
} else if (retval == 0) {
|
||||
dev_dbg(dev, "--> PCI %s\n",
|
||||
pci_power_name(pci_dev->current_state));
|
||||
} else {
|
||||
suspend_report_result(pci_prepare_to_sleep, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
powermac_set_asic(pci_dev, 0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hcd_pci_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
||||
powermac_set_asic(pci_dev, 1);
|
||||
|
||||
/* Go back to D0 and disable remote wakeup */
|
||||
pci_back_from_sleep(pci_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hcd_pci_resume(struct device *dev)
|
||||
{
|
||||
return resume_common(dev, PM_EVENT_RESUME);
|
||||
}
|
||||
|
||||
static int hcd_pci_restore(struct device *dev)
|
||||
{
|
||||
return resume_common(dev, PM_EVENT_RESTORE);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define hcd_pci_suspend NULL
|
||||
#define hcd_pci_suspend_noirq NULL
|
||||
#define hcd_pci_resume_noirq NULL
|
||||
#define hcd_pci_resume NULL
|
||||
#define hcd_pci_restore NULL
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int hcd_pci_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = suspend_common(dev, true);
|
||||
if (retval == 0)
|
||||
powermac_set_asic(to_pci_dev(dev), 0);
|
||||
dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hcd_pci_runtime_resume(struct device *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
powermac_set_asic(to_pci_dev(dev), 1);
|
||||
retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
|
||||
dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define hcd_pci_runtime_suspend NULL
|
||||
#define hcd_pci_runtime_resume NULL
|
||||
|
||||
#endif /* CONFIG_PM_RUNTIME */
|
||||
|
||||
const struct dev_pm_ops usb_hcd_pci_pm_ops = {
|
||||
.suspend = hcd_pci_suspend,
|
||||
.suspend_noirq = hcd_pci_suspend_noirq,
|
||||
.resume_noirq = hcd_pci_resume_noirq,
|
||||
.resume = hcd_pci_resume,
|
||||
.freeze = check_root_hub_suspended,
|
||||
.freeze_noirq = check_root_hub_suspended,
|
||||
.thaw_noirq = NULL,
|
||||
.thaw = NULL,
|
||||
.poweroff = hcd_pci_suspend,
|
||||
.poweroff_noirq = hcd_pci_suspend_noirq,
|
||||
.restore_noirq = hcd_pci_resume_noirq,
|
||||
.restore = hcd_pci_restore,
|
||||
.runtime_suspend = hcd_pci_runtime_suspend,
|
||||
.runtime_resume = hcd_pci_runtime_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
|
||||
|
||||
#endif /* CONFIG_PM */
|
2967
drivers/usb/core/hcd.c
Normal file
2967
drivers/usb/core/hcd.c
Normal file
File diff suppressed because it is too large
Load diff
5726
drivers/usb/core/hub.c
Normal file
5726
drivers/usb/core/hub.c
Normal file
File diff suppressed because it is too large
Load diff
158
drivers/usb/core/hub.h
Normal file
158
drivers/usb/core/hub.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* usb hub driver head file
|
||||
*
|
||||
* Copyright (C) 1999 Linus Torvalds
|
||||
* Copyright (C) 1999 Johannes Erdfelt
|
||||
* Copyright (C) 1999 Gregory P. Smith
|
||||
* Copyright (C) 2001 Brad Hards (bhards@bigpond.net.au)
|
||||
* Copyright (C) 2012 Intel Corp (tianyu.lan@intel.com)
|
||||
*
|
||||
* move struct usb_hub to this file.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 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.
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ch11.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "usb.h"
|
||||
|
||||
struct usb_hub {
|
||||
struct device *intfdev; /* the "interface" device */
|
||||
struct usb_device *hdev;
|
||||
struct kref kref;
|
||||
struct urb *urb; /* for interrupt polling pipe */
|
||||
|
||||
/* buffer for urb ... with extra space in case of babble */
|
||||
u8 (*buffer)[8];
|
||||
union {
|
||||
struct usb_hub_status hub;
|
||||
struct usb_port_status port;
|
||||
} *status; /* buffer for status reports */
|
||||
struct mutex status_mutex; /* for the status buffer */
|
||||
|
||||
int error; /* last reported error */
|
||||
int nerrors; /* track consecutive errors */
|
||||
|
||||
unsigned long event_bits[1]; /* status change bitmask */
|
||||
unsigned long change_bits[1]; /* ports with logical connect
|
||||
status change */
|
||||
unsigned long removed_bits[1]; /* ports with a "removed"
|
||||
device present */
|
||||
unsigned long wakeup_bits[1]; /* ports that have signaled
|
||||
remote wakeup */
|
||||
unsigned long power_bits[1]; /* ports that are powered */
|
||||
unsigned long child_usage_bits[1]; /* ports powered on for
|
||||
children */
|
||||
unsigned long warm_reset_bits[1]; /* ports requesting warm
|
||||
reset recovery */
|
||||
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
||||
#error event_bits[] is too short!
|
||||
#endif
|
||||
|
||||
struct usb_hub_descriptor *descriptor; /* class descriptor */
|
||||
struct usb_tt tt; /* Transaction Translator */
|
||||
|
||||
unsigned mA_per_port; /* current for each child */
|
||||
#ifdef CONFIG_PM
|
||||
unsigned wakeup_enabled_descendants;
|
||||
#endif
|
||||
|
||||
unsigned limited_power:1;
|
||||
unsigned quiescing:1;
|
||||
unsigned disconnected:1;
|
||||
unsigned in_reset:1;
|
||||
|
||||
unsigned quirk_check_port_auto_suspend:1;
|
||||
|
||||
unsigned has_indicators:1;
|
||||
u8 indicator[USB_MAXCHILDREN];
|
||||
struct delayed_work leds;
|
||||
struct delayed_work init_work;
|
||||
struct work_struct events;
|
||||
struct usb_port **ports;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct usb port - kernel's representation of a usb port
|
||||
* @child: usb device attached to the port
|
||||
* @dev: generic device interface
|
||||
* @port_owner: port's owner
|
||||
* @peer: related usb2 and usb3 ports (share the same connector)
|
||||
* @req: default pm qos request for hubs without port power control
|
||||
* @connect_type: port's connect type
|
||||
* @location: opaque representation of platform connector location
|
||||
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
|
||||
* @portnum: port index num based one
|
||||
* @is_superspeed cache super-speed status
|
||||
*/
|
||||
struct usb_port {
|
||||
struct usb_device *child;
|
||||
struct device dev;
|
||||
struct usb_dev_state *port_owner;
|
||||
struct usb_port *peer;
|
||||
struct dev_pm_qos_request *req;
|
||||
enum usb_port_connect_type connect_type;
|
||||
usb_port_location_t location;
|
||||
struct mutex status_lock;
|
||||
u8 portnum;
|
||||
unsigned int is_superspeed:1;
|
||||
};
|
||||
|
||||
#define to_usb_port(_dev) \
|
||||
container_of(_dev, struct usb_port, dev)
|
||||
|
||||
extern int usb_hub_create_port_device(struct usb_hub *hub,
|
||||
int port1);
|
||||
extern void usb_hub_remove_port_device(struct usb_hub *hub,
|
||||
int port1);
|
||||
extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
|
||||
int port1, bool set);
|
||||
extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
|
||||
extern int hub_port_debounce(struct usb_hub *hub, int port1,
|
||||
bool must_be_connected);
|
||||
extern int usb_clear_port_feature(struct usb_device *hdev,
|
||||
int port1, int feature);
|
||||
|
||||
static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
|
||||
{
|
||||
__le16 hcs;
|
||||
|
||||
if (!hub)
|
||||
return false;
|
||||
hcs = hub->descriptor->wHubCharacteristics;
|
||||
return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
|
||||
}
|
||||
|
||||
static inline int hub_is_superspeed(struct usb_device *hdev)
|
||||
{
|
||||
return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
|
||||
}
|
||||
|
||||
static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
|
||||
{
|
||||
unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
|
||||
|
||||
/* Wait at least 100 msec for power to become stable */
|
||||
return max(delay, 100U);
|
||||
}
|
||||
|
||||
static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
|
||||
int port1)
|
||||
{
|
||||
return hub_port_debounce(hub, port1, true);
|
||||
}
|
||||
|
||||
static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
|
||||
int port1)
|
||||
{
|
||||
return hub_port_debounce(hub, port1, false);
|
||||
}
|
||||
|
1992
drivers/usb/core/message.c
Normal file
1992
drivers/usb/core/message.c
Normal file
File diff suppressed because it is too large
Load diff
69
drivers/usb/core/notify.c
Normal file
69
drivers/usb/core/notify.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* All the USB notify logic
|
||||
*
|
||||
* (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
|
||||
*
|
||||
* notifier functions originally based on those in kernel/sys.c
|
||||
* but fixed up to not be so broken.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "usb.h"
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(usb_notifier_list);
|
||||
|
||||
/**
|
||||
* usb_register_notify - register a notifier callback whenever a usb change happens
|
||||
* @nb: pointer to the notifier block for the callback events.
|
||||
*
|
||||
* These changes are either USB devices or busses being added or removed.
|
||||
*/
|
||||
void usb_register_notify(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_register(&usb_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_register_notify);
|
||||
|
||||
/**
|
||||
* usb_unregister_notify - unregister a notifier callback
|
||||
* @nb: pointer to the notifier block for the callback events.
|
||||
*
|
||||
* usb_register_notify() must have been previously called for this function
|
||||
* to work properly.
|
||||
*/
|
||||
void usb_unregister_notify(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&usb_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unregister_notify);
|
||||
|
||||
|
||||
void usb_notify_add_device(struct usb_device *udev)
|
||||
{
|
||||
blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
|
||||
}
|
||||
|
||||
void usb_notify_remove_device(struct usb_device *udev)
|
||||
{
|
||||
/* Protect against simultaneous usbfs open */
|
||||
mutex_lock(&usbfs_mutex);
|
||||
blocking_notifier_call_chain(&usb_notifier_list,
|
||||
USB_DEVICE_REMOVE, udev);
|
||||
mutex_unlock(&usbfs_mutex);
|
||||
}
|
||||
|
||||
void usb_notify_add_bus(struct usb_bus *ubus)
|
||||
{
|
||||
blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus);
|
||||
}
|
||||
|
||||
void usb_notify_remove_bus(struct usb_bus *ubus)
|
||||
{
|
||||
blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus);
|
||||
}
|
110
drivers/usb/core/otg_whitelist.h
Normal file
110
drivers/usb/core/otg_whitelist.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* drivers/usb/core/otg_whitelist.h
|
||||
*
|
||||
* Copyright (C) 2004 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This OTG and Embedded Host Whitelist is "Targeted Peripheral List".
|
||||
* It should mostly use of USB_DEVICE() or USB_DEVICE_VER() entries..
|
||||
*
|
||||
* YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING!
|
||||
*/
|
||||
|
||||
static struct usb_device_id whitelist_table [] = {
|
||||
|
||||
/* hubs are optional in OTG, but very handy ... */
|
||||
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
|
||||
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
|
||||
|
||||
#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */
|
||||
/* FIXME actually, printers are NOT supposed to use device classes;
|
||||
* they're supposed to use interface classes...
|
||||
*/
|
||||
{ USB_DEVICE_INFO(7, 1, 1) },
|
||||
{ USB_DEVICE_INFO(7, 1, 2) },
|
||||
{ USB_DEVICE_INFO(7, 1, 3) },
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_NET_CDCETHER
|
||||
/* Linux-USB CDC Ethernet gadget */
|
||||
{ USB_DEVICE(0x0525, 0xa4a1), },
|
||||
/* Linux-USB CDC Ethernet + RNDIS gadget */
|
||||
{ USB_DEVICE(0x0525, 0xa4a2), },
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE)
|
||||
/* gadget zero, for testing */
|
||||
{ USB_DEVICE(0x0525, 0xa4a0), },
|
||||
#endif
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
static int is_targeted(struct usb_device *dev)
|
||||
{
|
||||
struct usb_device_id *id = whitelist_table;
|
||||
|
||||
/* HNP test device is _never_ targeted (see OTG spec 6.6.6) */
|
||||
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
|
||||
le16_to_cpu(dev->descriptor.idProduct) == 0xbadd))
|
||||
return 0;
|
||||
|
||||
/* OTG PET device is always targeted (see OTG 2.0 ECN 6.4.2) */
|
||||
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
|
||||
le16_to_cpu(dev->descriptor.idProduct) == 0x0200))
|
||||
return 1;
|
||||
|
||||
/* NOTE: can't use usb_match_id() since interface caches
|
||||
* aren't set up yet. this is cut/paste from that code.
|
||||
*/
|
||||
for (id = whitelist_table; id->match_flags; id++) {
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
|
||||
id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
|
||||
id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
|
||||
continue;
|
||||
|
||||
/* No need to test id->bcdDevice_lo != 0, since 0 is never
|
||||
greater than any unsigned number. */
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
|
||||
(id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
|
||||
(id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
|
||||
(id->bDeviceClass != dev->descriptor.bDeviceClass))
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
|
||||
(id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
|
||||
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
|
||||
continue;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* add other match criteria here ... */
|
||||
|
||||
|
||||
/* OTG MESSAGE: report errors here, customize to match your product */
|
||||
dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
487
drivers/usb/core/port.c
Normal file
487
drivers/usb/core/port.c
Normal file
|
@ -0,0 +1,487 @@
|
|||
/*
|
||||
* usb port device code
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corp
|
||||
*
|
||||
* Author: Lan Tianyu <tianyu.lan@intel.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 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#include "hub.h"
|
||||
|
||||
static int usb_port_block_power_off;
|
||||
|
||||
static const struct attribute_group *port_dev_group[];
|
||||
|
||||
static ssize_t connect_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
char *result;
|
||||
|
||||
switch (port_dev->connect_type) {
|
||||
case USB_PORT_CONNECT_TYPE_HOT_PLUG:
|
||||
result = "hotplug";
|
||||
break;
|
||||
case USB_PORT_CONNECT_TYPE_HARD_WIRED:
|
||||
result = "hardwired";
|
||||
break;
|
||||
case USB_PORT_NOT_USED:
|
||||
result = "not used";
|
||||
break;
|
||||
default:
|
||||
result = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", result);
|
||||
}
|
||||
static DEVICE_ATTR_RO(connect_type);
|
||||
|
||||
static struct attribute *port_dev_attrs[] = {
|
||||
&dev_attr_connect_type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group port_dev_attr_grp = {
|
||||
.attrs = port_dev_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *port_dev_group[] = {
|
||||
&port_dev_attr_grp,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void usb_port_device_release(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
|
||||
kfree(port_dev->req);
|
||||
kfree(port_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int usb_port_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
struct usb_device *hdev = to_usb_device(dev->parent->parent);
|
||||
struct usb_interface *intf = to_usb_interface(dev->parent);
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
struct usb_device *udev = port_dev->child;
|
||||
struct usb_port *peer = port_dev->peer;
|
||||
int port1 = port_dev->portnum;
|
||||
int retval;
|
||||
|
||||
if (!hub)
|
||||
return -EINVAL;
|
||||
if (hub->in_reset) {
|
||||
set_bit(port1, hub->power_bits);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power on our usb3 peer before this usb2 port to prevent a usb3
|
||||
* device from degrading to its usb2 connection
|
||||
*/
|
||||
if (!port_dev->is_superspeed && peer)
|
||||
pm_runtime_get_sync(&peer->dev);
|
||||
|
||||
usb_autopm_get_interface(intf);
|
||||
retval = usb_hub_set_port_power(hdev, hub, port1, true);
|
||||
msleep(hub_power_on_good_delay(hub));
|
||||
if (udev && !retval) {
|
||||
/*
|
||||
* Our preference is to simply wait for the port to reconnect,
|
||||
* as that is the lowest latency method to restart the port.
|
||||
* However, there are cases where toggling port power results in
|
||||
* the host port and the device port getting out of sync causing
|
||||
* a link training live lock. Upon timeout, flag the port as
|
||||
* needing warm reset recovery (to be performed later by
|
||||
* usb_port_resume() as requested via usb_wakeup_notification())
|
||||
*/
|
||||
if (hub_port_debounce_be_connected(hub, port1) < 0) {
|
||||
dev_dbg(&port_dev->dev, "reconnect timeout\n");
|
||||
if (hub_is_superspeed(hdev))
|
||||
set_bit(port1, hub->warm_reset_bits);
|
||||
}
|
||||
|
||||
/* Force the child awake to revalidate after the power loss. */
|
||||
if (!test_and_set_bit(port1, hub->child_usage_bits)) {
|
||||
pm_runtime_get_noresume(&port_dev->dev);
|
||||
pm_request_resume(&udev->dev);
|
||||
}
|
||||
}
|
||||
|
||||
usb_autopm_put_interface(intf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int usb_port_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
struct usb_device *hdev = to_usb_device(dev->parent->parent);
|
||||
struct usb_interface *intf = to_usb_interface(dev->parent);
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
struct usb_port *peer = port_dev->peer;
|
||||
int port1 = port_dev->portnum;
|
||||
int retval;
|
||||
|
||||
if (!hub)
|
||||
return -EINVAL;
|
||||
if (hub->in_reset)
|
||||
return -EBUSY;
|
||||
|
||||
if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
|
||||
== PM_QOS_FLAGS_ALL)
|
||||
return -EAGAIN;
|
||||
|
||||
if (usb_port_block_power_off)
|
||||
return -EBUSY;
|
||||
|
||||
usb_autopm_get_interface(intf);
|
||||
retval = usb_hub_set_port_power(hdev, hub, port1, false);
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
|
||||
if (!port_dev->is_superspeed)
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
|
||||
usb_autopm_put_interface(intf);
|
||||
|
||||
/*
|
||||
* Our peer usb3 port may now be able to suspend, so
|
||||
* asynchronously queue a suspend request to observe that this
|
||||
* usb2 port is now off.
|
||||
*/
|
||||
if (!port_dev->is_superspeed && peer)
|
||||
pm_runtime_put(&peer->dev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops usb_port_pm_ops = {
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
.runtime_suspend = usb_port_runtime_suspend,
|
||||
.runtime_resume = usb_port_runtime_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct device_type usb_port_device_type = {
|
||||
.name = "usb_port",
|
||||
.release = usb_port_device_release,
|
||||
.pm = &usb_port_pm_ops,
|
||||
};
|
||||
|
||||
static struct device_driver usb_port_driver = {
|
||||
.name = "usb",
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int link_peers(struct usb_port *left, struct usb_port *right)
|
||||
{
|
||||
struct usb_port *ss_port, *hs_port;
|
||||
int rc;
|
||||
|
||||
if (left->peer == right && right->peer == left)
|
||||
return 0;
|
||||
|
||||
if (left->peer || right->peer) {
|
||||
struct usb_port *lpeer = left->peer;
|
||||
struct usb_port *rpeer = right->peer;
|
||||
char *method;
|
||||
|
||||
if (left->location && left->location == right->location)
|
||||
method = "location";
|
||||
else
|
||||
method = "default";
|
||||
|
||||
pr_warn("usb: failed to peer %s and %s by %s (%s:%s) (%s:%s)\n",
|
||||
dev_name(&left->dev), dev_name(&right->dev), method,
|
||||
dev_name(&left->dev),
|
||||
lpeer ? dev_name(&lpeer->dev) : "none",
|
||||
dev_name(&right->dev),
|
||||
rpeer ? dev_name(&rpeer->dev) : "none");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
rc = sysfs_create_link(&left->dev.kobj, &right->dev.kobj, "peer");
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = sysfs_create_link(&right->dev.kobj, &left->dev.kobj, "peer");
|
||||
if (rc) {
|
||||
sysfs_remove_link(&left->dev.kobj, "peer");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to wake the HiSpeed port to make sure we don't race
|
||||
* setting ->peer with usb_port_runtime_suspend(). Otherwise we
|
||||
* may miss a suspend event for the SuperSpeed port.
|
||||
*/
|
||||
if (left->is_superspeed) {
|
||||
ss_port = left;
|
||||
WARN_ON(right->is_superspeed);
|
||||
hs_port = right;
|
||||
} else {
|
||||
ss_port = right;
|
||||
WARN_ON(!right->is_superspeed);
|
||||
hs_port = left;
|
||||
}
|
||||
pm_runtime_get_sync(&hs_port->dev);
|
||||
|
||||
left->peer = right;
|
||||
right->peer = left;
|
||||
|
||||
/*
|
||||
* The SuperSpeed reference is dropped when the HiSpeed port in
|
||||
* this relationship suspends, i.e. when it is safe to allow a
|
||||
* SuperSpeed connection to drop since there is no risk of a
|
||||
* device degrading to its powered-off HiSpeed connection.
|
||||
*
|
||||
* Also, drop the HiSpeed ref taken above.
|
||||
*/
|
||||
pm_runtime_get_sync(&ss_port->dev);
|
||||
pm_runtime_put(&hs_port->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void link_peers_report(struct usb_port *left, struct usb_port *right)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = link_peers(left, right);
|
||||
if (rc == 0) {
|
||||
dev_dbg(&left->dev, "peered to %s\n", dev_name(&right->dev));
|
||||
} else {
|
||||
dev_warn(&left->dev, "failed to peer to %s (%d)\n",
|
||||
dev_name(&right->dev), rc);
|
||||
pr_warn_once("usb: port power management may be unreliable\n");
|
||||
usb_port_block_power_off = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void unlink_peers(struct usb_port *left, struct usb_port *right)
|
||||
{
|
||||
struct usb_port *ss_port, *hs_port;
|
||||
|
||||
WARN(right->peer != left || left->peer != right,
|
||||
"%s and %s are not peers?\n",
|
||||
dev_name(&left->dev), dev_name(&right->dev));
|
||||
|
||||
/*
|
||||
* We wake the HiSpeed port to make sure we don't race its
|
||||
* usb_port_runtime_resume() event which takes a SuperSpeed ref
|
||||
* when ->peer is !NULL.
|
||||
*/
|
||||
if (left->is_superspeed) {
|
||||
ss_port = left;
|
||||
hs_port = right;
|
||||
} else {
|
||||
ss_port = right;
|
||||
hs_port = left;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&hs_port->dev);
|
||||
|
||||
sysfs_remove_link(&left->dev.kobj, "peer");
|
||||
right->peer = NULL;
|
||||
sysfs_remove_link(&right->dev.kobj, "peer");
|
||||
left->peer = NULL;
|
||||
|
||||
/* Drop the SuperSpeed ref held on behalf of the active HiSpeed port */
|
||||
pm_runtime_put(&ss_port->dev);
|
||||
|
||||
/* Drop the ref taken above */
|
||||
pm_runtime_put(&hs_port->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* For each usb hub device in the system check to see if it is in the
|
||||
* peer domain of the given port_dev, and if it is check to see if it
|
||||
* has a port that matches the given port by location
|
||||
*/
|
||||
static int match_location(struct usb_device *peer_hdev, void *p)
|
||||
{
|
||||
int port1;
|
||||
struct usb_hcd *hcd, *peer_hcd;
|
||||
struct usb_port *port_dev = p, *peer;
|
||||
struct usb_hub *peer_hub = usb_hub_to_struct_hub(peer_hdev);
|
||||
struct usb_device *hdev = to_usb_device(port_dev->dev.parent->parent);
|
||||
|
||||
if (!peer_hub)
|
||||
return 0;
|
||||
|
||||
hcd = bus_to_hcd(hdev->bus);
|
||||
peer_hcd = bus_to_hcd(peer_hdev->bus);
|
||||
/* peer_hcd is provisional until we verify it against the known peer */
|
||||
if (peer_hcd != hcd->shared_hcd)
|
||||
return 0;
|
||||
|
||||
for (port1 = 1; port1 <= peer_hdev->maxchild; port1++) {
|
||||
peer = peer_hub->ports[port1 - 1];
|
||||
if (peer && peer->location == port_dev->location) {
|
||||
link_peers_report(port_dev, peer);
|
||||
return 1; /* done */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the peer port either via explicit platform firmware "location"
|
||||
* data, the peer hcd for root hubs, or the upstream peer relationship
|
||||
* for all other hubs.
|
||||
*/
|
||||
static void find_and_link_peer(struct usb_hub *hub, int port1)
|
||||
{
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1], *peer;
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct usb_device *peer_hdev;
|
||||
struct usb_hub *peer_hub;
|
||||
|
||||
/*
|
||||
* If location data is available then we can only peer this port
|
||||
* by a location match, not the default peer (lest we create a
|
||||
* situation where we need to go back and undo a default peering
|
||||
* when the port is later peered by location data)
|
||||
*/
|
||||
if (port_dev->location) {
|
||||
/* we link the peer in match_location() if found */
|
||||
usb_for_each_dev(port_dev, match_location);
|
||||
return;
|
||||
} else if (!hdev->parent) {
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
struct usb_hcd *peer_hcd = hcd->shared_hcd;
|
||||
|
||||
if (!peer_hcd)
|
||||
return;
|
||||
|
||||
peer_hdev = peer_hcd->self.root_hub;
|
||||
} else {
|
||||
struct usb_port *upstream;
|
||||
struct usb_device *parent = hdev->parent;
|
||||
struct usb_hub *parent_hub = usb_hub_to_struct_hub(parent);
|
||||
|
||||
if (!parent_hub)
|
||||
return;
|
||||
|
||||
upstream = parent_hub->ports[hdev->portnum - 1];
|
||||
if (!upstream || !upstream->peer)
|
||||
return;
|
||||
|
||||
peer_hdev = upstream->peer->child;
|
||||
}
|
||||
|
||||
peer_hub = usb_hub_to_struct_hub(peer_hdev);
|
||||
if (!peer_hub || port1 > peer_hdev->maxchild)
|
||||
return;
|
||||
|
||||
/*
|
||||
* we found a valid default peer, last check is to make sure it
|
||||
* does not have location data
|
||||
*/
|
||||
peer = peer_hub->ports[port1 - 1];
|
||||
if (peer && peer->location == 0)
|
||||
link_peers_report(port_dev, peer);
|
||||
}
|
||||
|
||||
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
|
||||
{
|
||||
struct usb_port *port_dev;
|
||||
int retval;
|
||||
|
||||
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
|
||||
if (!port_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL);
|
||||
if (!port_dev->req) {
|
||||
kfree(port_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hub->ports[port1 - 1] = port_dev;
|
||||
port_dev->portnum = port1;
|
||||
set_bit(port1, hub->power_bits);
|
||||
port_dev->dev.parent = hub->intfdev;
|
||||
port_dev->dev.groups = port_dev_group;
|
||||
port_dev->dev.type = &usb_port_device_type;
|
||||
port_dev->dev.driver = &usb_port_driver;
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
port_dev->is_superspeed = 1;
|
||||
dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
|
||||
port1);
|
||||
mutex_init(&port_dev->status_lock);
|
||||
retval = device_register(&port_dev->dev);
|
||||
if (retval) {
|
||||
put_device(&port_dev->dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Set default policy of port-poweroff disabled. */
|
||||
retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req,
|
||||
DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF);
|
||||
if (retval < 0) {
|
||||
device_unregister(&port_dev->dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
find_and_link_peer(hub, port1);
|
||||
|
||||
/*
|
||||
* Enable runtime pm and hold a refernce that hub_configure()
|
||||
* will drop once the PM_QOS_NO_POWER_OFF flag state has been set
|
||||
* and the hub has been fully registered (hdev->maxchild set).
|
||||
*/
|
||||
pm_runtime_set_active(&port_dev->dev);
|
||||
pm_runtime_get_noresume(&port_dev->dev);
|
||||
pm_runtime_enable(&port_dev->dev);
|
||||
device_enable_async_suspend(&port_dev->dev);
|
||||
|
||||
/*
|
||||
* Keep hidden the ability to enable port-poweroff if the hub
|
||||
* does not support power switching.
|
||||
*/
|
||||
if (!hub_is_port_power_switchable(hub))
|
||||
return 0;
|
||||
|
||||
/* Attempt to let userspace take over the policy. */
|
||||
retval = dev_pm_qos_expose_flags(&port_dev->dev,
|
||||
PM_QOS_FLAG_NO_POWER_OFF);
|
||||
if (retval < 0) {
|
||||
dev_warn(&port_dev->dev, "failed to expose pm_qos_no_poweroff\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Userspace owns the policy, drop the kernel 'no_poweroff' request. */
|
||||
retval = dev_pm_qos_remove_request(port_dev->req);
|
||||
if (retval >= 0) {
|
||||
kfree(port_dev->req);
|
||||
port_dev->req = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
|
||||
{
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1];
|
||||
struct usb_port *peer;
|
||||
|
||||
peer = port_dev->peer;
|
||||
if (peer)
|
||||
unlink_peers(port_dev, peer);
|
||||
device_unregister(&port_dev->dev);
|
||||
}
|
309
drivers/usb/core/quirks.c
Normal file
309
drivers/usb/core/quirks.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* USB device quirk handling logic and table
|
||||
*
|
||||
* Copyright (c) 2007 Oliver Neukum
|
||||
* Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 2.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include "usb.h"
|
||||
|
||||
/* Lists of quirky USB devices, split in device quirks and interface quirks.
|
||||
* Device quirks are applied at the very beginning of the enumeration process,
|
||||
* right after reading the device descriptor. They can thus only match on device
|
||||
* information.
|
||||
*
|
||||
* Interface quirks are applied after reading all the configuration descriptors.
|
||||
* They can match on both device and interface information.
|
||||
*
|
||||
* Note that the DELAY_INIT and HONOR_BNUMINTERFACES quirks do not make sense as
|
||||
* interface quirks, as they only influence the enumeration process which is run
|
||||
* before processing the interface quirks.
|
||||
*
|
||||
* Please keep the lists ordered by:
|
||||
* 1) Vendor ID
|
||||
* 2) Product ID
|
||||
* 3) Class ID
|
||||
*/
|
||||
static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* CBM - Flash disk */
|
||||
{ USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* HP 5300/5370C scanner */
|
||||
{ USB_DEVICE(0x03f0, 0x0701), .driver_info =
|
||||
USB_QUIRK_STRING_FETCH_255 },
|
||||
|
||||
/* Creative SB Audigy 2 NX */
|
||||
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Microsoft Wireless Laser Mouse 6000 Receiver */
|
||||
{ USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Microsoft LifeCam-VX700 v2.0 */
|
||||
{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech HD Pro Webcams C920 and C930e */
|
||||
{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
{ USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
|
||||
/* Logitech Quickcam Fusion */
|
||||
{ USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Quickcam Orbit MP */
|
||||
{ USB_DEVICE(0x046d, 0x08c2), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Quickcam Pro for Notebook */
|
||||
{ USB_DEVICE(0x046d, 0x08c3), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Quickcam Pro 5000 */
|
||||
{ USB_DEVICE(0x046d, 0x08c5), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Quickcam OEM Dell Notebook */
|
||||
{ USB_DEVICE(0x046d, 0x08c6), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Quickcam OEM Cisco VT Camera II */
|
||||
{ USB_DEVICE(0x046d, 0x08c7), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Harmony 700-series */
|
||||
{ USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
|
||||
/* Philips PSC805 audio device */
|
||||
{ USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Artisman Watchdog Dongle */
|
||||
{ USB_DEVICE(0x04b4, 0x0526), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
||||
/* Microchip Joss Optical infrared touchboard device */
|
||||
{ USB_DEVICE(0x04d8, 0x000c), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
||||
/* CarrolTouch 4000U */
|
||||
{ USB_DEVICE(0x04e7, 0x0009), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* CarrolTouch 4500U */
|
||||
{ USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Samsung Android phone modem - ID conflict with SPH-I500 */
|
||||
{ USB_DEVICE(0x04e8, 0x6601), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
||||
/* Elan Touchscreen */
|
||||
{ USB_DEVICE(0x04f3, 0x0089), .driver_info =
|
||||
USB_QUIRK_DEVICE_QUALIFIER },
|
||||
|
||||
{ USB_DEVICE(0x04f3, 0x009b), .driver_info =
|
||||
USB_QUIRK_DEVICE_QUALIFIER },
|
||||
|
||||
{ USB_DEVICE(0x04f3, 0x010c), .driver_info =
|
||||
USB_QUIRK_DEVICE_QUALIFIER },
|
||||
|
||||
{ USB_DEVICE(0x04f3, 0x016f), .driver_info =
|
||||
USB_QUIRK_DEVICE_QUALIFIER },
|
||||
|
||||
/* Roland SC-8820 */
|
||||
{ USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Edirol SD-20 */
|
||||
{ USB_DEVICE(0x0582, 0x0027), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Alcor Micro Corp. Hub */
|
||||
{ USB_DEVICE(0x058f, 0x9254), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* appletouch */
|
||||
{ USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Avision AV600U */
|
||||
{ USB_DEVICE(0x0638, 0x0a13), .driver_info =
|
||||
USB_QUIRK_STRING_FETCH_255 },
|
||||
|
||||
/* Saitek Cyborg Gold Joystick */
|
||||
{ USB_DEVICE(0x06a3, 0x0006), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
||||
/* Guillemot Webcam Hercules Dualpix Exchange (2nd ID) */
|
||||
{ USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Guillemot Webcam Hercules Dualpix Exchange*/
|
||||
{ USB_DEVICE(0x06f8, 0x3005), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Midiman M-Audio Keystation 88es */
|
||||
{ USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* M-Systems Flash Disk Pioneers */
|
||||
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Keytouch QWERTY Panel keyboard */
|
||||
{ USB_DEVICE(0x0926, 0x3333), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
||||
/* X-Rite/Gretag-Macbeth Eye-One Pro display colorimeter */
|
||||
{ USB_DEVICE(0x0971, 0x2000), .driver_info = USB_QUIRK_NO_SET_INTF },
|
||||
|
||||
/* Broadcom BCM92035DGROM BT dongle */
|
||||
{ USB_DEVICE(0x0a5c, 0x2021), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* MAYA44USB sound device */
|
||||
{ USB_DEVICE(0x0a92, 0x0091), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Action Semiconductor flash disk */
|
||||
{ USB_DEVICE(0x10d6, 0x2200), .driver_info =
|
||||
USB_QUIRK_STRING_FETCH_255 },
|
||||
|
||||
/* SKYMEDI USB_DRIVE */
|
||||
{ USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Razer - Razer Blade Keyboard */
|
||||
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
|
||||
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
|
||||
|
||||
/* BUILDWIN Photo Frame */
|
||||
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
|
||||
USB_QUIRK_HONOR_BNUMINTERFACES },
|
||||
|
||||
/* INTEL VALUE SSD */
|
||||
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* USB3503 */
|
||||
{ USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* ASUS Base Station(T100) */
|
||||
{ USB_DEVICE(0x0b05, 0x17e0), .driver_info =
|
||||
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
|
||||
|
||||
/* Protocol and OTG Electrical Test Device */
|
||||
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
|
||||
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
||||
static const struct usb_device_id usb_interface_quirk_list[] = {
|
||||
/* Logitech UVC Cameras */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x046d, USB_CLASS_VIDEO, 1, 0),
|
||||
.driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
||||
static const struct usb_device_id usb_amd_resume_quirk_list[] = {
|
||||
/* Lenovo Mouse with Pixart controller */
|
||||
{ USB_DEVICE(0x17ef, 0x602e), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Pixart Mouse */
|
||||
{ USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
{ USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Logitech Optical Mouse M90/M100 */
|
||||
{ USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
||||
static bool usb_match_any_interface(struct usb_device *udev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) {
|
||||
struct usb_host_config *cfg = &udev->config[i];
|
||||
unsigned int j;
|
||||
|
||||
for (j = 0; j < cfg->desc.bNumInterfaces; ++j) {
|
||||
struct usb_interface_cache *cache;
|
||||
struct usb_host_interface *intf;
|
||||
|
||||
cache = cfg->intf_cache[j];
|
||||
if (cache->num_altsetting == 0)
|
||||
continue;
|
||||
|
||||
intf = &cache->altsetting[0];
|
||||
if (usb_match_one_id_intf(udev, intf, id))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int usb_amd_resume_quirk(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
/* The device should be attached directly to root hub */
|
||||
if (udev->level == 1 && hcd->amd_resume_bug == 1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 __usb_detect_quirks(struct usb_device *udev,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
u32 quirks = 0;
|
||||
|
||||
for (; id->match_flags; id++) {
|
||||
if (!usb_match_device(udev, id))
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) &&
|
||||
!usb_match_any_interface(udev, id))
|
||||
continue;
|
||||
|
||||
quirks |= (u32)(id->driver_info);
|
||||
}
|
||||
|
||||
return quirks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect any quirks the device has, and do any housekeeping for it if needed.
|
||||
*/
|
||||
void usb_detect_quirks(struct usb_device *udev)
|
||||
{
|
||||
udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
|
||||
|
||||
/*
|
||||
* Pixart-based mice would trigger remote wakeup issue on AMD
|
||||
* Yangtze chipset, so set them as RESET_RESUME flag.
|
||||
*/
|
||||
if (usb_amd_resume_quirk(udev))
|
||||
udev->quirks |= __usb_detect_quirks(udev,
|
||||
usb_amd_resume_quirk_list);
|
||||
|
||||
if (udev->quirks)
|
||||
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
|
||||
udev->quirks);
|
||||
|
||||
#ifdef CONFIG_USB_DEFAULT_PERSIST
|
||||
if (!(udev->quirks & USB_QUIRK_RESET))
|
||||
udev->persist_enabled = 1;
|
||||
#else
|
||||
/* Hubs are automatically enabled for USB-PERSIST */
|
||||
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
udev->persist_enabled = 1;
|
||||
#endif /* CONFIG_USB_DEFAULT_PERSIST */
|
||||
}
|
||||
|
||||
void usb_detect_interface_quirks(struct usb_device *udev)
|
||||
{
|
||||
u32 quirks;
|
||||
|
||||
quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
|
||||
if (quirks == 0)
|
||||
return;
|
||||
|
||||
dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n",
|
||||
quirks);
|
||||
udev->quirks |= quirks;
|
||||
}
|
1002
drivers/usb/core/sysfs.c
Normal file
1002
drivers/usb/core/sysfs.c
Normal file
File diff suppressed because it is too large
Load diff
969
drivers/usb/core/urb.c
Normal file
969
drivers/usb/core/urb.c
Normal file
|
@ -0,0 +1,969 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#define to_urb(d) container_of(d, struct urb, kref)
|
||||
|
||||
|
||||
static void urb_destroy(struct kref *kref)
|
||||
{
|
||||
struct urb *urb = to_urb(kref);
|
||||
|
||||
if (urb->transfer_flags & URB_FREE_BUFFER)
|
||||
kfree(urb->transfer_buffer);
|
||||
|
||||
kfree(urb);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_init_urb - initializes a urb so that it can be used by a USB driver
|
||||
* @urb: pointer to the urb to initialize
|
||||
*
|
||||
* Initializes a urb so that the USB subsystem can use it properly.
|
||||
*
|
||||
* If a urb is created with a call to usb_alloc_urb() it is not
|
||||
* necessary to call this function. Only use this if you allocate the
|
||||
* space for a struct urb on your own. If you call this function, be
|
||||
* careful when freeing the memory for your urb that it is no longer in
|
||||
* use by the USB core.
|
||||
*
|
||||
* Only use this function if you _really_ understand what you are doing.
|
||||
*/
|
||||
void usb_init_urb(struct urb *urb)
|
||||
{
|
||||
if (urb) {
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
kref_init(&urb->kref);
|
||||
INIT_LIST_HEAD(&urb->anchor_list);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_init_urb);
|
||||
|
||||
/**
|
||||
* usb_alloc_urb - creates a new urb for a USB driver to use
|
||||
* @iso_packets: number of iso packets for this urb
|
||||
* @mem_flags: the type of memory to allocate, see kmalloc() for a list of
|
||||
* valid options for this.
|
||||
*
|
||||
* Creates an urb for the USB driver to use, initializes a few internal
|
||||
* structures, increments the usage counter, and returns a pointer to it.
|
||||
*
|
||||
* If the driver want to use this urb for interrupt, control, or bulk
|
||||
* endpoints, pass '0' as the number of iso packets.
|
||||
*
|
||||
* The driver must call usb_free_urb() when it is finished with the urb.
|
||||
*
|
||||
* Return: A pointer to the new urb, or %NULL if no memory is available.
|
||||
*/
|
||||
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
|
||||
{
|
||||
struct urb *urb;
|
||||
|
||||
urb = kmalloc(sizeof(struct urb) +
|
||||
iso_packets * sizeof(struct usb_iso_packet_descriptor),
|
||||
mem_flags);
|
||||
if (!urb) {
|
||||
printk(KERN_ERR "alloc_urb: kmalloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
usb_init_urb(urb);
|
||||
return urb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_alloc_urb);
|
||||
|
||||
/**
|
||||
* usb_free_urb - frees the memory used by a urb when all users of it are finished
|
||||
* @urb: pointer to the urb to free, may be NULL
|
||||
*
|
||||
* Must be called when a user of a urb is finished with it. When the last user
|
||||
* of the urb calls this function, the memory of the urb is freed.
|
||||
*
|
||||
* Note: The transfer buffer associated with the urb is not freed unless the
|
||||
* URB_FREE_BUFFER transfer flag is set.
|
||||
*/
|
||||
void usb_free_urb(struct urb *urb)
|
||||
{
|
||||
if (urb)
|
||||
kref_put(&urb->kref, urb_destroy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_free_urb);
|
||||
|
||||
/**
|
||||
* usb_get_urb - increments the reference count of the urb
|
||||
* @urb: pointer to the urb to modify, may be NULL
|
||||
*
|
||||
* This must be called whenever a urb is transferred from a device driver to a
|
||||
* host controller driver. This allows proper reference counting to happen
|
||||
* for urbs.
|
||||
*
|
||||
* Return: A pointer to the urb with the incremented reference counter.
|
||||
*/
|
||||
struct urb *usb_get_urb(struct urb *urb)
|
||||
{
|
||||
if (urb)
|
||||
kref_get(&urb->kref);
|
||||
return urb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_urb);
|
||||
|
||||
/**
|
||||
* usb_anchor_urb - anchors an URB while it is processed
|
||||
* @urb: pointer to the urb to anchor
|
||||
* @anchor: pointer to the anchor
|
||||
*
|
||||
* This can be called to have access to URBs which are to be executed
|
||||
* without bothering to track them
|
||||
*/
|
||||
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
usb_get_urb(urb);
|
||||
list_add_tail(&urb->anchor_list, &anchor->urb_list);
|
||||
urb->anchor = anchor;
|
||||
|
||||
if (unlikely(anchor->poisoned)) {
|
||||
atomic_inc(&urb->reject);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_anchor_urb);
|
||||
|
||||
static int usb_anchor_check_wakeup(struct usb_anchor *anchor)
|
||||
{
|
||||
return atomic_read(&anchor->suspend_wakeups) == 0 &&
|
||||
list_empty(&anchor->urb_list);
|
||||
}
|
||||
|
||||
/* Callers must hold anchor->lock */
|
||||
static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
||||
{
|
||||
urb->anchor = NULL;
|
||||
list_del(&urb->anchor_list);
|
||||
usb_put_urb(urb);
|
||||
if (usb_anchor_check_wakeup(anchor))
|
||||
wake_up(&anchor->wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_unanchor_urb - unanchors an URB
|
||||
* @urb: pointer to the urb to anchor
|
||||
*
|
||||
* Call this to stop the system keeping track of this URB
|
||||
*/
|
||||
void usb_unanchor_urb(struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_anchor *anchor;
|
||||
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
anchor = urb->anchor;
|
||||
if (!anchor)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
/*
|
||||
* At this point, we could be competing with another thread which
|
||||
* has the same intention. To protect the urb from being unanchored
|
||||
* twice, only the winner of the race gets the job.
|
||||
*/
|
||||
if (likely(anchor == urb->anchor))
|
||||
__usb_unanchor_urb(urb, anchor);
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unanchor_urb);
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* usb_submit_urb - issue an asynchronous transfer request for an endpoint
|
||||
* @urb: pointer to the urb describing the request
|
||||
* @mem_flags: the type of memory to allocate, see kmalloc() for a list
|
||||
* of valid options for this.
|
||||
*
|
||||
* This submits a transfer request, and transfers control of the URB
|
||||
* describing that request to the USB subsystem. Request completion will
|
||||
* be indicated later, asynchronously, by calling the completion handler.
|
||||
* The three types of completion are success, error, and unlink
|
||||
* (a software-induced fault, also called "request cancellation").
|
||||
*
|
||||
* URBs may be submitted in interrupt context.
|
||||
*
|
||||
* The caller must have correctly initialized the URB before submitting
|
||||
* it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are
|
||||
* available to ensure that most fields are correctly initialized, for
|
||||
* the particular kind of transfer, although they will not initialize
|
||||
* any transfer flags.
|
||||
*
|
||||
* If the submission is successful, the complete() callback from the URB
|
||||
* will be called exactly once, when the USB core and Host Controller Driver
|
||||
* (HCD) are finished with the URB. When the completion function is called,
|
||||
* control of the URB is returned to the device driver which issued the
|
||||
* request. The completion handler may then immediately free or reuse that
|
||||
* URB.
|
||||
*
|
||||
* With few exceptions, USB device drivers should never access URB fields
|
||||
* provided by usbcore or the HCD until its complete() is called.
|
||||
* The exceptions relate to periodic transfer scheduling. For both
|
||||
* interrupt and isochronous urbs, as part of successful URB submission
|
||||
* urb->interval is modified to reflect the actual transfer period used
|
||||
* (normally some power of two units). And for isochronous urbs,
|
||||
* urb->start_frame is modified to reflect when the URB's transfers were
|
||||
* scheduled to start.
|
||||
*
|
||||
* Not all isochronous transfer scheduling policies will work, but most
|
||||
* host controller drivers should easily handle ISO queues going from now
|
||||
* until 10-200 msec into the future. Drivers should try to keep at
|
||||
* least one or two msec of data in the queue; many controllers require
|
||||
* that new transfers start at least 1 msec in the future when they are
|
||||
* added. If the driver is unable to keep up and the queue empties out,
|
||||
* the behavior for new submissions is governed by the URB_ISO_ASAP flag.
|
||||
* If the flag is set, or if the queue is idle, then the URB is always
|
||||
* assigned to the first available (and not yet expired) slot in the
|
||||
* endpoint's schedule. If the flag is not set and the queue is active
|
||||
* then the URB is always assigned to the next slot in the schedule
|
||||
* following the end of the endpoint's previous URB, even if that slot is
|
||||
* in the past. When a packet is assigned in this way to a slot that has
|
||||
* already expired, the packet is not transmitted and the corresponding
|
||||
* usb_iso_packet_descriptor's status field will return -EXDEV. If this
|
||||
* would happen to all the packets in the URB, submission fails with a
|
||||
* -EXDEV error code.
|
||||
*
|
||||
* For control endpoints, the synchronous usb_control_msg() call is
|
||||
* often used (in non-interrupt context) instead of this call.
|
||||
* That is often used through convenience wrappers, for the requests
|
||||
* that are standardized in the USB 2.0 specification. For bulk
|
||||
* endpoints, a synchronous usb_bulk_msg() call is available.
|
||||
*
|
||||
* Return:
|
||||
* 0 on successful submissions. A negative error number otherwise.
|
||||
*
|
||||
* Request Queuing:
|
||||
*
|
||||
* URBs may be submitted to endpoints before previous ones complete, to
|
||||
* minimize the impact of interrupt latencies and system overhead on data
|
||||
* throughput. With that queuing policy, an endpoint's queue would never
|
||||
* be empty. This is required for continuous isochronous data streams,
|
||||
* and may also be required for some kinds of interrupt transfers. Such
|
||||
* queuing also maximizes bandwidth utilization by letting USB controllers
|
||||
* start work on later requests before driver software has finished the
|
||||
* completion processing for earlier (successful) requests.
|
||||
*
|
||||
* As of Linux 2.6, all USB endpoint transfer queues support depths greater
|
||||
* than one. This was previously a HCD-specific behavior, except for ISO
|
||||
* transfers. Non-isochronous endpoint queues are inactive during cleanup
|
||||
* after faults (transfer errors or cancellation).
|
||||
*
|
||||
* Reserved Bandwidth Transfers:
|
||||
*
|
||||
* Periodic transfers (interrupt or isochronous) are performed repeatedly,
|
||||
* using the interval specified in the urb. Submitting the first urb to
|
||||
* the endpoint reserves the bandwidth necessary to make those transfers.
|
||||
* If the USB subsystem can't allocate sufficient bandwidth to perform
|
||||
* the periodic request, submitting such a periodic request should fail.
|
||||
*
|
||||
* For devices under xHCI, the bandwidth is reserved at configuration time, or
|
||||
* when the alt setting is selected. If there is not enough bus bandwidth, the
|
||||
* configuration/alt setting request will fail. Therefore, submissions to
|
||||
* periodic endpoints on devices under xHCI should never fail due to bandwidth
|
||||
* constraints.
|
||||
*
|
||||
* Device drivers must explicitly request that repetition, by ensuring that
|
||||
* some URB is always on the endpoint's queue (except possibly for short
|
||||
* periods during completion callbacks). When there is no longer an urb
|
||||
* queued, the endpoint's bandwidth reservation is canceled. This means
|
||||
* drivers can use their completion handlers to ensure they keep bandwidth
|
||||
* they need, by reinitializing and resubmitting the just-completed urb
|
||||
* until the driver longer needs that periodic bandwidth.
|
||||
*
|
||||
* Memory Flags:
|
||||
*
|
||||
* The general rules for how to decide which mem_flags to use
|
||||
* are the same as for kmalloc. There are four
|
||||
* different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and
|
||||
* GFP_ATOMIC.
|
||||
*
|
||||
* GFP_NOFS is not ever used, as it has not been implemented yet.
|
||||
*
|
||||
* GFP_ATOMIC is used when
|
||||
* (a) you are inside a completion handler, an interrupt, bottom half,
|
||||
* tasklet or timer, or
|
||||
* (b) you are holding a spinlock or rwlock (does not apply to
|
||||
* semaphores), or
|
||||
* (c) current->state != TASK_RUNNING, this is the case only after
|
||||
* you've changed it.
|
||||
*
|
||||
* GFP_NOIO is used in the block io path and error handling of storage
|
||||
* devices.
|
||||
*
|
||||
* All other situations use GFP_KERNEL.
|
||||
*
|
||||
* Some more specific rules for mem_flags can be inferred, such as
|
||||
* (1) start_xmit, timeout, and receive methods of network drivers must
|
||||
* use GFP_ATOMIC (they are called with a spinlock held);
|
||||
* (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also
|
||||
* called with a spinlock held);
|
||||
* (3) If you use a kernel thread with a network driver you must use
|
||||
* GFP_NOIO, unless (b) or (c) apply;
|
||||
* (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)
|
||||
* apply or your are in a storage driver's block io path;
|
||||
* (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and
|
||||
* (6) changing firmware on a running storage or net device uses
|
||||
* GFP_NOIO, unless b) or c) apply
|
||||
*
|
||||
*/
|
||||
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
{
|
||||
static int pipetypes[4] = {
|
||||
PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
|
||||
};
|
||||
int xfertype, max;
|
||||
struct usb_device *dev;
|
||||
struct usb_host_endpoint *ep;
|
||||
int is_out;
|
||||
unsigned int allowed;
|
||||
|
||||
if (!urb || !urb->complete)
|
||||
return -EINVAL;
|
||||
if (urb->hcpriv) {
|
||||
WARN_ONCE(1, "URB %p submitted while active\n", urb);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev = urb->dev;
|
||||
if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
|
||||
return -ENODEV;
|
||||
|
||||
/* For now, get the endpoint from the pipe. Eventually drivers
|
||||
* will be required to set urb->ep directly and we will eliminate
|
||||
* urb->pipe.
|
||||
*/
|
||||
ep = usb_pipe_endpoint(dev, urb->pipe);
|
||||
if (!ep)
|
||||
return -ENOENT;
|
||||
|
||||
urb->ep = ep;
|
||||
urb->status = -EINPROGRESS;
|
||||
urb->actual_length = 0;
|
||||
|
||||
/* Lots of sanity checks, so HCDs can rely on clean data
|
||||
* and don't need to duplicate tests
|
||||
*/
|
||||
xfertype = usb_endpoint_type(&ep->desc);
|
||||
if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
|
||||
struct usb_ctrlrequest *setup =
|
||||
(struct usb_ctrlrequest *) urb->setup_packet;
|
||||
|
||||
if (!setup)
|
||||
return -ENOEXEC;
|
||||
is_out = !(setup->bRequestType & USB_DIR_IN) ||
|
||||
!setup->wLength;
|
||||
} else {
|
||||
is_out = usb_endpoint_dir_out(&ep->desc);
|
||||
}
|
||||
|
||||
/* Clear the internal flags and cache the direction for later use */
|
||||
urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
|
||||
URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
|
||||
URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
|
||||
URB_DMA_SG_COMBINED);
|
||||
urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
|
||||
|
||||
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
|
||||
dev->state < USB_STATE_CONFIGURED)
|
||||
return -ENODEV;
|
||||
|
||||
max = usb_endpoint_maxp(&ep->desc);
|
||||
if (max <= 0) {
|
||||
dev_dbg(&dev->dev,
|
||||
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
|
||||
usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
|
||||
__func__, max);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* periodic transfers limit size per frame/uframe,
|
||||
* but drivers only control those sizes for ISO.
|
||||
* while we're checking, initialize return status.
|
||||
*/
|
||||
if (xfertype == USB_ENDPOINT_XFER_ISOC) {
|
||||
int n, len;
|
||||
|
||||
/* SuperSpeed isoc endpoints have up to 16 bursts of up to
|
||||
* 3 packets each
|
||||
*/
|
||||
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;
|
||||
max *= mult;
|
||||
}
|
||||
|
||||
/* "high bandwidth" mode, 1-3 packets/uframe? */
|
||||
if (dev->speed == USB_SPEED_HIGH) {
|
||||
int mult = 1 + ((max >> 11) & 0x03);
|
||||
max &= 0x07ff;
|
||||
max *= mult;
|
||||
}
|
||||
|
||||
if (urb->number_of_packets <= 0)
|
||||
return -EINVAL;
|
||||
for (n = 0; n < urb->number_of_packets; n++) {
|
||||
len = urb->iso_frame_desc[n].length;
|
||||
if (len < 0 || len > max)
|
||||
return -EMSGSIZE;
|
||||
urb->iso_frame_desc[n].status = -EXDEV;
|
||||
urb->iso_frame_desc[n].actual_length = 0;
|
||||
}
|
||||
} else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint &&
|
||||
dev->speed != USB_SPEED_WIRELESS) {
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(urb->sg, sg, urb->num_sgs - 1, i)
|
||||
if (sg->length % max)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the I/O buffer must be mapped/unmapped, except when length=0 */
|
||||
if (urb->transfer_buffer_length > INT_MAX)
|
||||
return -EMSGSIZE;
|
||||
|
||||
/*
|
||||
* stuff that drivers shouldn't do, but which shouldn't
|
||||
* cause problems in HCDs if they get it wrong.
|
||||
*/
|
||||
|
||||
/* Check that the pipe's type matches the endpoint's type */
|
||||
if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
|
||||
dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
|
||||
usb_pipetype(urb->pipe), pipetypes[xfertype]);
|
||||
|
||||
/* Check against a simple/standard policy */
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
|
||||
URB_FREE_BUFFER);
|
||||
switch (xfertype) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
if (is_out)
|
||||
allowed |= URB_ZERO_PACKET;
|
||||
/* FALLTHROUGH */
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
allowed |= URB_NO_FSBR; /* only affects UHCI */
|
||||
/* FALLTHROUGH */
|
||||
default: /* all non-iso endpoints */
|
||||
if (!is_out)
|
||||
allowed |= URB_SHORT_NOT_OK;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
allowed |= URB_ISO_ASAP;
|
||||
break;
|
||||
}
|
||||
allowed &= urb->transfer_flags;
|
||||
|
||||
/* warn if submitter gave bogus flags */
|
||||
if (allowed != urb->transfer_flags)
|
||||
dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
|
||||
urb->transfer_flags, allowed);
|
||||
|
||||
/*
|
||||
* Force periodic transfer intervals to be legal values that are
|
||||
* a power of two (so HCDs don't need to).
|
||||
*
|
||||
* FIXME want bus->{intr,iso}_sched_horizon values here. Each HC
|
||||
* supports different values... this uses EHCI/UHCI defaults (and
|
||||
* EHCI can use smaller non-default values).
|
||||
*/
|
||||
switch (xfertype) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* too small? */
|
||||
switch (dev->speed) {
|
||||
case USB_SPEED_WIRELESS:
|
||||
if ((urb->interval < 6)
|
||||
&& (xfertype == USB_ENDPOINT_XFER_INT))
|
||||
return -EINVAL;
|
||||
default:
|
||||
if (urb->interval <= 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
/* too big? */
|
||||
switch (dev->speed) {
|
||||
case USB_SPEED_SUPER: /* units are 125us */
|
||||
/* Handle up to 2^(16-1) microframes */
|
||||
if (urb->interval > (1 << 15))
|
||||
return -EINVAL;
|
||||
max = 1 << 15;
|
||||
break;
|
||||
case USB_SPEED_WIRELESS:
|
||||
if (urb->interval > 16)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case USB_SPEED_HIGH: /* units are microframes */
|
||||
/* NOTE usb handles 2^15 */
|
||||
if (urb->interval > (1024 * 8))
|
||||
urb->interval = 1024 * 8;
|
||||
max = 1024 * 8;
|
||||
break;
|
||||
case USB_SPEED_FULL: /* units are frames/msec */
|
||||
case USB_SPEED_LOW:
|
||||
if (xfertype == USB_ENDPOINT_XFER_INT) {
|
||||
if (urb->interval > 255)
|
||||
return -EINVAL;
|
||||
/* NOTE ohci only handles up to 32 */
|
||||
max = 128;
|
||||
} else {
|
||||
if (urb->interval > 1024)
|
||||
urb->interval = 1024;
|
||||
/* NOTE usb and ohci handle up to 2^15 */
|
||||
max = 1024;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (dev->speed != USB_SPEED_WIRELESS) {
|
||||
/* Round down to a power of 2, no more than max */
|
||||
urb->interval = min(max, 1 << ilog2(urb->interval));
|
||||
}
|
||||
}
|
||||
|
||||
return usb_hcd_submit_urb(urb, mem_flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_submit_urb);
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* usb_unlink_urb - abort/cancel a transfer request for an endpoint
|
||||
* @urb: pointer to urb describing a previously submitted request,
|
||||
* may be NULL
|
||||
*
|
||||
* This routine cancels an in-progress request. URBs complete only once
|
||||
* per submission, and may be canceled only once per submission.
|
||||
* Successful cancellation means termination of @urb will be expedited
|
||||
* and the completion handler will be called with a status code
|
||||
* indicating that the request has been canceled (rather than any other
|
||||
* code).
|
||||
*
|
||||
* Drivers should not call this routine or related routines, such as
|
||||
* usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect
|
||||
* method has returned. The disconnect function should synchronize with
|
||||
* a driver's I/O routines to insure that all URB-related activity has
|
||||
* completed before it returns.
|
||||
*
|
||||
* This request is asynchronous, however the HCD might call the ->complete()
|
||||
* callback during unlink. Therefore when drivers call usb_unlink_urb(), they
|
||||
* must not hold any locks that may be taken by the completion function.
|
||||
* Success is indicated by returning -EINPROGRESS, at which time the URB will
|
||||
* probably not yet have been given back to the device driver. When it is
|
||||
* eventually called, the completion function will see @urb->status ==
|
||||
* -ECONNRESET.
|
||||
* Failure is indicated by usb_unlink_urb() returning any other value.
|
||||
* Unlinking will fail when @urb is not currently "linked" (i.e., it was
|
||||
* never submitted, or it was unlinked before, or the hardware is already
|
||||
* finished with it), even if the completion handler has not yet run.
|
||||
*
|
||||
* The URB must not be deallocated while this routine is running. In
|
||||
* particular, when a driver calls this routine, it must insure that the
|
||||
* completion handler cannot deallocate the URB.
|
||||
*
|
||||
* Return: -EINPROGRESS on success. See description for other values on
|
||||
* failure.
|
||||
*
|
||||
* Unlinking and Endpoint Queues:
|
||||
*
|
||||
* [The behaviors and guarantees described below do not apply to virtual
|
||||
* root hubs but only to endpoint queues for physical USB devices.]
|
||||
*
|
||||
* Host Controller Drivers (HCDs) place all the URBs for a particular
|
||||
* endpoint in a queue. Normally the queue advances as the controller
|
||||
* hardware processes each request. But when an URB terminates with an
|
||||
* error its queue generally stops (see below), at least until that URB's
|
||||
* completion routine returns. It is guaranteed that a stopped queue
|
||||
* will not restart until all its unlinked URBs have been fully retired,
|
||||
* with their completion routines run, even if that's not until some time
|
||||
* after the original completion handler returns. The same behavior and
|
||||
* guarantee apply when an URB terminates because it was unlinked.
|
||||
*
|
||||
* Bulk and interrupt endpoint queues are guaranteed to stop whenever an
|
||||
* URB terminates with any sort of error, including -ECONNRESET, -ENOENT,
|
||||
* and -EREMOTEIO. Control endpoint queues behave the same way except
|
||||
* that they are not guaranteed to stop for -EREMOTEIO errors. Queues
|
||||
* for isochronous endpoints are treated differently, because they must
|
||||
* advance at fixed rates. Such queues do not stop when an URB
|
||||
* encounters an error or is unlinked. An unlinked isochronous URB may
|
||||
* leave a gap in the stream of packets; it is undefined whether such
|
||||
* gaps can be filled in.
|
||||
*
|
||||
* Note that early termination of an URB because a short packet was
|
||||
* received will generate a -EREMOTEIO error if and only if the
|
||||
* URB_SHORT_NOT_OK flag is set. By setting this flag, USB device
|
||||
* drivers can build deep queues for large or complex bulk transfers
|
||||
* and clean them up reliably after any sort of aborted transfer by
|
||||
* unlinking all pending URBs at the first fault.
|
||||
*
|
||||
* When a control URB terminates with an error other than -EREMOTEIO, it
|
||||
* is quite likely that the status stage of the transfer will not take
|
||||
* place.
|
||||
*/
|
||||
int usb_unlink_urb(struct urb *urb)
|
||||
{
|
||||
if (!urb)
|
||||
return -EINVAL;
|
||||
if (!urb->dev)
|
||||
return -ENODEV;
|
||||
if (!urb->ep)
|
||||
return -EIDRM;
|
||||
return usb_hcd_unlink_urb(urb, -ECONNRESET);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlink_urb);
|
||||
|
||||
/**
|
||||
* usb_kill_urb - cancel a transfer request and wait for it to finish
|
||||
* @urb: pointer to URB describing a previously submitted request,
|
||||
* may be NULL
|
||||
*
|
||||
* This routine cancels an in-progress request. It is guaranteed that
|
||||
* upon return all completion handlers will have finished and the URB
|
||||
* will be totally idle and available for reuse. These features make
|
||||
* this an ideal way to stop I/O in a disconnect() callback or close()
|
||||
* function. If the request has not already finished or been unlinked
|
||||
* the completion handler will see urb->status == -ENOENT.
|
||||
*
|
||||
* While the routine is running, attempts to resubmit the URB will fail
|
||||
* with error -EPERM. Thus even if the URB's completion handler always
|
||||
* tries to resubmit, it will not succeed and the URB will become idle.
|
||||
*
|
||||
* The URB must not be deallocated while this routine is running. In
|
||||
* particular, when a driver calls this routine, it must insure that the
|
||||
* completion handler cannot deallocate the URB.
|
||||
*
|
||||
* This routine may not be used in an interrupt context (such as a bottom
|
||||
* half or a completion handler), or when holding a spinlock, or in other
|
||||
* situations where the caller can't schedule().
|
||||
*
|
||||
* This routine should not be called by a driver after its disconnect
|
||||
* method has returned.
|
||||
*/
|
||||
void usb_kill_urb(struct urb *urb)
|
||||
{
|
||||
might_sleep();
|
||||
if (!(urb && urb->dev && urb->ep))
|
||||
return;
|
||||
atomic_inc(&urb->reject);
|
||||
|
||||
usb_hcd_unlink_urb(urb, -ENOENT);
|
||||
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
|
||||
|
||||
atomic_dec(&urb->reject);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_kill_urb);
|
||||
|
||||
/**
|
||||
* usb_poison_urb - reliably kill a transfer and prevent further use of an URB
|
||||
* @urb: pointer to URB describing a previously submitted request,
|
||||
* may be NULL
|
||||
*
|
||||
* This routine cancels an in-progress request. It is guaranteed that
|
||||
* upon return all completion handlers will have finished and the URB
|
||||
* will be totally idle and cannot be reused. These features make
|
||||
* this an ideal way to stop I/O in a disconnect() callback.
|
||||
* If the request has not already finished or been unlinked
|
||||
* the completion handler will see urb->status == -ENOENT.
|
||||
*
|
||||
* After and while the routine runs, attempts to resubmit the URB will fail
|
||||
* with error -EPERM. Thus even if the URB's completion handler always
|
||||
* tries to resubmit, it will not succeed and the URB will become idle.
|
||||
*
|
||||
* The URB must not be deallocated while this routine is running. In
|
||||
* particular, when a driver calls this routine, it must insure that the
|
||||
* completion handler cannot deallocate the URB.
|
||||
*
|
||||
* This routine may not be used in an interrupt context (such as a bottom
|
||||
* half or a completion handler), or when holding a spinlock, or in other
|
||||
* situations where the caller can't schedule().
|
||||
*
|
||||
* This routine should not be called by a driver after its disconnect
|
||||
* method has returned.
|
||||
*/
|
||||
void usb_poison_urb(struct urb *urb)
|
||||
{
|
||||
might_sleep();
|
||||
if (!urb)
|
||||
return;
|
||||
atomic_inc(&urb->reject);
|
||||
|
||||
if (!urb->dev || !urb->ep)
|
||||
return;
|
||||
|
||||
usb_hcd_unlink_urb(urb, -ENOENT);
|
||||
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_poison_urb);
|
||||
|
||||
void usb_unpoison_urb(struct urb *urb)
|
||||
{
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
atomic_dec(&urb->reject);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unpoison_urb);
|
||||
|
||||
/**
|
||||
* usb_block_urb - reliably prevent further use of an URB
|
||||
* @urb: pointer to URB to be blocked, may be NULL
|
||||
*
|
||||
* After the routine has run, attempts to resubmit the URB will fail
|
||||
* with error -EPERM. Thus even if the URB's completion handler always
|
||||
* tries to resubmit, it will not succeed and the URB will become idle.
|
||||
*
|
||||
* The URB must not be deallocated while this routine is running. In
|
||||
* particular, when a driver calls this routine, it must insure that the
|
||||
* completion handler cannot deallocate the URB.
|
||||
*/
|
||||
void usb_block_urb(struct urb *urb)
|
||||
{
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
atomic_inc(&urb->reject);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_block_urb);
|
||||
|
||||
/**
|
||||
* usb_kill_anchored_urbs - cancel transfer requests en masse
|
||||
* @anchor: anchor the requests are bound to
|
||||
*
|
||||
* this allows all outstanding URBs to be killed starting
|
||||
* from the back of the queue
|
||||
*
|
||||
* This routine should not be called by a driver after its disconnect
|
||||
* method has returned.
|
||||
*/
|
||||
void usb_kill_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
struct urb *victim;
|
||||
|
||||
spin_lock_irq(&anchor->lock);
|
||||
while (!list_empty(&anchor->urb_list)) {
|
||||
victim = list_entry(anchor->urb_list.prev, struct urb,
|
||||
anchor_list);
|
||||
/* we must make sure the URB isn't freed before we kill it*/
|
||||
usb_get_urb(victim);
|
||||
spin_unlock_irq(&anchor->lock);
|
||||
/* this will unanchor the URB */
|
||||
usb_kill_urb(victim);
|
||||
usb_put_urb(victim);
|
||||
spin_lock_irq(&anchor->lock);
|
||||
}
|
||||
spin_unlock_irq(&anchor->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
|
||||
|
||||
|
||||
/**
|
||||
* usb_poison_anchored_urbs - cease all traffic from an anchor
|
||||
* @anchor: anchor the requests are bound to
|
||||
*
|
||||
* this allows all outstanding URBs to be poisoned starting
|
||||
* from the back of the queue. Newly added URBs will also be
|
||||
* poisoned
|
||||
*
|
||||
* This routine should not be called by a driver after its disconnect
|
||||
* method has returned.
|
||||
*/
|
||||
void usb_poison_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
struct urb *victim;
|
||||
|
||||
spin_lock_irq(&anchor->lock);
|
||||
anchor->poisoned = 1;
|
||||
while (!list_empty(&anchor->urb_list)) {
|
||||
victim = list_entry(anchor->urb_list.prev, struct urb,
|
||||
anchor_list);
|
||||
/* we must make sure the URB isn't freed before we kill it*/
|
||||
usb_get_urb(victim);
|
||||
spin_unlock_irq(&anchor->lock);
|
||||
/* this will unanchor the URB */
|
||||
usb_poison_urb(victim);
|
||||
usb_put_urb(victim);
|
||||
spin_lock_irq(&anchor->lock);
|
||||
}
|
||||
spin_unlock_irq(&anchor->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
|
||||
|
||||
/**
|
||||
* usb_unpoison_anchored_urbs - let an anchor be used successfully again
|
||||
* @anchor: anchor the requests are bound to
|
||||
*
|
||||
* Reverses the effect of usb_poison_anchored_urbs
|
||||
* the anchor can be used normally after it returns
|
||||
*/
|
||||
void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct urb *lazarus;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) {
|
||||
usb_unpoison_urb(lazarus);
|
||||
}
|
||||
anchor->poisoned = 0;
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
|
||||
/**
|
||||
* usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
|
||||
* @anchor: anchor the requests are bound to
|
||||
*
|
||||
* this allows all outstanding URBs to be unlinked starting
|
||||
* from the back of the queue. This function is asynchronous.
|
||||
* The unlinking is just triggered. It may happen after this
|
||||
* function has returned.
|
||||
*
|
||||
* This routine should not be called by a driver after its disconnect
|
||||
* method has returned.
|
||||
*/
|
||||
void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
struct urb *victim;
|
||||
|
||||
while ((victim = usb_get_from_anchor(anchor)) != NULL) {
|
||||
usb_unlink_urb(victim);
|
||||
usb_put_urb(victim);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
|
||||
|
||||
/**
|
||||
* usb_anchor_suspend_wakeups
|
||||
* @anchor: the anchor you want to suspend wakeups on
|
||||
*
|
||||
* Call this to stop the last urb being unanchored from waking up any
|
||||
* usb_wait_anchor_empty_timeout waiters. This is used in the hcd urb give-
|
||||
* back path to delay waking up until after the completion handler has run.
|
||||
*/
|
||||
void usb_anchor_suspend_wakeups(struct usb_anchor *anchor)
|
||||
{
|
||||
if (anchor)
|
||||
atomic_inc(&anchor->suspend_wakeups);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_anchor_suspend_wakeups);
|
||||
|
||||
/**
|
||||
* usb_anchor_resume_wakeups
|
||||
* @anchor: the anchor you want to resume wakeups on
|
||||
*
|
||||
* Allow usb_wait_anchor_empty_timeout waiters to be woken up again, and
|
||||
* wake up any current waiters if the anchor is empty.
|
||||
*/
|
||||
void usb_anchor_resume_wakeups(struct usb_anchor *anchor)
|
||||
{
|
||||
if (!anchor)
|
||||
return;
|
||||
|
||||
atomic_dec(&anchor->suspend_wakeups);
|
||||
if (usb_anchor_check_wakeup(anchor))
|
||||
wake_up(&anchor->wait);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_anchor_resume_wakeups);
|
||||
|
||||
/**
|
||||
* usb_wait_anchor_empty_timeout - wait for an anchor to be unused
|
||||
* @anchor: the anchor you want to become unused
|
||||
* @timeout: how long you are willing to wait in milliseconds
|
||||
*
|
||||
* Call this is you want to be sure all an anchor's
|
||||
* URBs have finished
|
||||
*
|
||||
* Return: Non-zero if the anchor became unused. Zero on timeout.
|
||||
*/
|
||||
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
|
||||
unsigned int timeout)
|
||||
{
|
||||
return wait_event_timeout(anchor->wait,
|
||||
usb_anchor_check_wakeup(anchor),
|
||||
msecs_to_jiffies(timeout));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
|
||||
|
||||
/**
|
||||
* usb_get_from_anchor - get an anchor's oldest urb
|
||||
* @anchor: the anchor whose urb you want
|
||||
*
|
||||
* This will take the oldest urb from an anchor,
|
||||
* unanchor and return it
|
||||
*
|
||||
* Return: The oldest urb from @anchor, or %NULL if @anchor has no
|
||||
* urbs associated with it.
|
||||
*/
|
||||
struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
|
||||
{
|
||||
struct urb *victim;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
if (!list_empty(&anchor->urb_list)) {
|
||||
victim = list_entry(anchor->urb_list.next, struct urb,
|
||||
anchor_list);
|
||||
usb_get_urb(victim);
|
||||
__usb_unanchor_urb(victim, anchor);
|
||||
} else {
|
||||
victim = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
|
||||
return victim;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_get_from_anchor);
|
||||
|
||||
/**
|
||||
* usb_scuttle_anchored_urbs - unanchor all an anchor's urbs
|
||||
* @anchor: the anchor whose urbs you want to unanchor
|
||||
*
|
||||
* use this to get rid of all an anchor's urbs
|
||||
*/
|
||||
void usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
struct urb *victim;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
while (!list_empty(&anchor->urb_list)) {
|
||||
victim = list_entry(anchor->urb_list.prev, struct urb,
|
||||
anchor_list);
|
||||
__usb_unanchor_urb(victim, anchor);
|
||||
}
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
|
||||
|
||||
/**
|
||||
* usb_anchor_empty - is an anchor empty
|
||||
* @anchor: the anchor you want to query
|
||||
*
|
||||
* Return: 1 if the anchor has no urbs associated with it.
|
||||
*/
|
||||
int usb_anchor_empty(struct usb_anchor *anchor)
|
||||
{
|
||||
return list_empty(&anchor->urb_list);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usb_anchor_empty);
|
||||
|
228
drivers/usb/core/usb-acpi.c
Normal file
228
drivers/usb/core/usb-acpi.c
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* USB-ACPI glue code
|
||||
*
|
||||
* Copyright 2012 Red Hat <mjg@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 2.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "hub.h"
|
||||
|
||||
/**
|
||||
* usb_acpi_power_manageable - check whether usb port has
|
||||
* acpi power resource.
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @index: port index based zero
|
||||
*
|
||||
* Return true if the port has acpi power resource and false if no.
|
||||
*/
|
||||
bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
|
||||
{
|
||||
acpi_handle port_handle;
|
||||
int port1 = index + 1;
|
||||
|
||||
port_handle = usb_get_hub_port_acpi_handle(hdev,
|
||||
port1);
|
||||
if (port_handle)
|
||||
return acpi_bus_power_manageable(port_handle);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
|
||||
|
||||
/**
|
||||
* usb_acpi_set_power_state - control usb port's power via acpi power
|
||||
* resource
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @index: port index based zero
|
||||
* @enable: power state expected to be set
|
||||
*
|
||||
* Notice to use usb_acpi_power_manageable() to check whether the usb port
|
||||
* has acpi power resource before invoking this function.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
|
||||
{
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
struct usb_port *port_dev;
|
||||
acpi_handle port_handle;
|
||||
unsigned char state;
|
||||
int port1 = index + 1;
|
||||
int error = -EINVAL;
|
||||
|
||||
if (!hub)
|
||||
return -ENODEV;
|
||||
port_dev = hub->ports[port1 - 1];
|
||||
|
||||
port_handle = (acpi_handle) usb_get_hub_port_acpi_handle(hdev, port1);
|
||||
if (!port_handle)
|
||||
return error;
|
||||
|
||||
if (enable)
|
||||
state = ACPI_STATE_D0;
|
||||
else
|
||||
state = ACPI_STATE_D3_COLD;
|
||||
|
||||
error = acpi_bus_set_power(port_handle, state);
|
||||
if (!error)
|
||||
dev_dbg(&port_dev->dev, "acpi: power was set to %d\n", enable);
|
||||
else
|
||||
dev_dbg(&port_dev->dev, "acpi: power failed to be set\n");
|
||||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
|
||||
|
||||
static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
|
||||
struct acpi_pld_info *pld)
|
||||
{
|
||||
enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *upc;
|
||||
acpi_status status;
|
||||
|
||||
/*
|
||||
* According to ACPI Spec 9.13. PLD indicates whether usb port is
|
||||
* user visible and _UPC indicates whether it is connectable. If
|
||||
* the port was visible and connectable, it could be freely connected
|
||||
* and disconnected with USB devices. If no visible and connectable,
|
||||
* a usb device is directly hard-wired to the port. If no visible and
|
||||
* no connectable, the port would be not used.
|
||||
*/
|
||||
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
|
||||
upc = buffer.pointer;
|
||||
if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
|
||||
|| upc->package.count != 4) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (upc->package.elements[0].integer.value)
|
||||
if (pld->user_visible)
|
||||
connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG;
|
||||
else
|
||||
connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED;
|
||||
else if (!pld->user_visible)
|
||||
connect_type = USB_PORT_NOT_USED;
|
||||
out:
|
||||
kfree(upc);
|
||||
return connect_type;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Private to usb-acpi, all the core needs to know is that
|
||||
* port_dev->location is non-zero when it has been set by the firmware.
|
||||
*/
|
||||
#define USB_ACPI_LOCATION_VALID (1 << 31)
|
||||
|
||||
static struct acpi_device *usb_acpi_find_companion(struct device *dev)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle *parent_handle;
|
||||
|
||||
/*
|
||||
* In the ACPI DSDT table, only usb root hub and usb ports are
|
||||
* acpi device nodes. The hierarchy like following.
|
||||
* Device (EHC1)
|
||||
* Device (HUBN)
|
||||
* Device (PR01)
|
||||
* Device (PR11)
|
||||
* Device (PR12)
|
||||
* Device (PR13)
|
||||
* ...
|
||||
* So all binding process is divided into two parts. binding
|
||||
* root hub and usb ports.
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
udev = to_usb_device(dev);
|
||||
if (udev->parent)
|
||||
return NULL;
|
||||
|
||||
/* root hub is only child (_ADR=0) under its parent, the HC */
|
||||
adev = ACPI_COMPANION(dev->parent);
|
||||
return acpi_find_child_device(adev, 0, false);
|
||||
} else if (is_usb_port(dev)) {
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
int port1 = port_dev->portnum;
|
||||
struct acpi_pld_info *pld;
|
||||
acpi_handle *handle;
|
||||
acpi_status status;
|
||||
|
||||
/* Get the struct usb_device point of port's hub */
|
||||
udev = to_usb_device(dev->parent->parent);
|
||||
|
||||
/*
|
||||
* The root hub ports' parent is the root hub. The non-root-hub
|
||||
* ports' parent is the parent hub port which the hub is
|
||||
* connected to.
|
||||
*/
|
||||
if (!udev->parent) {
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
int raw;
|
||||
|
||||
raw = usb_hcd_find_raw_port_number(hcd, port1);
|
||||
adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev),
|
||||
raw, false);
|
||||
if (!adev)
|
||||
return NULL;
|
||||
} else {
|
||||
parent_handle =
|
||||
usb_get_hub_port_acpi_handle(udev->parent,
|
||||
udev->portnum);
|
||||
if (!parent_handle)
|
||||
return NULL;
|
||||
|
||||
acpi_bus_get_device(parent_handle, &adev);
|
||||
adev = acpi_find_child_device(adev, port1, false);
|
||||
if (!adev)
|
||||
return NULL;
|
||||
}
|
||||
handle = adev->handle;
|
||||
status = acpi_get_physical_device_location(handle, &pld);
|
||||
if (ACPI_FAILURE(status) || !pld)
|
||||
return adev;
|
||||
|
||||
port_dev->location = USB_ACPI_LOCATION_VALID
|
||||
| pld->group_token << 8 | pld->group_position;
|
||||
port_dev->connect_type = usb_acpi_get_connect_type(handle, pld);
|
||||
ACPI_FREE(pld);
|
||||
|
||||
return adev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool usb_acpi_bus_match(struct device *dev)
|
||||
{
|
||||
return is_usb_device(dev) || is_usb_port(dev);
|
||||
}
|
||||
|
||||
static struct acpi_bus_type usb_acpi_bus = {
|
||||
.name = "USB",
|
||||
.match = usb_acpi_bus_match,
|
||||
.find_companion = usb_acpi_find_companion,
|
||||
};
|
||||
|
||||
int usb_acpi_register(void)
|
||||
{
|
||||
return register_acpi_bus_type(&usb_acpi_bus);
|
||||
}
|
||||
|
||||
void usb_acpi_unregister(void)
|
||||
{
|
||||
unregister_acpi_bus_type(&usb_acpi_bus);
|
||||
}
|
1123
drivers/usb/core/usb.c
Normal file
1123
drivers/usb/core/usb.c
Normal file
File diff suppressed because it is too large
Load diff
191
drivers/usb/core/usb.h
Normal file
191
drivers/usb/core/usb.h
Normal file
|
@ -0,0 +1,191 @@
|
|||
#include <linux/pm.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
struct usb_hub_descriptor;
|
||||
struct usb_dev_state;
|
||||
|
||||
/* Functions local to drivers/usb/core/ */
|
||||
|
||||
extern int usb_create_sysfs_dev_files(struct usb_device *dev);
|
||||
extern void usb_remove_sysfs_dev_files(struct usb_device *dev);
|
||||
extern void usb_create_sysfs_intf_files(struct usb_interface *intf);
|
||||
extern void usb_remove_sysfs_intf_files(struct usb_interface *intf);
|
||||
extern int usb_create_ep_devs(struct device *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev);
|
||||
extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint);
|
||||
|
||||
extern void usb_enable_endpoint(struct usb_device *dev,
|
||||
struct usb_host_endpoint *ep, bool reset_toggle);
|
||||
extern void usb_enable_interface(struct usb_device *dev,
|
||||
struct usb_interface *intf, bool reset_toggles);
|
||||
extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
|
||||
bool reset_hardware);
|
||||
extern void usb_disable_interface(struct usb_device *dev,
|
||||
struct usb_interface *intf, bool reset_hardware);
|
||||
extern void usb_release_interface_cache(struct kref *ref);
|
||||
extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
|
||||
extern int usb_deauthorize_device(struct usb_device *);
|
||||
extern int usb_authorize_device(struct usb_device *);
|
||||
extern void usb_detect_quirks(struct usb_device *udev);
|
||||
extern void usb_detect_interface_quirks(struct usb_device *udev);
|
||||
extern int usb_remove_device(struct usb_device *udev);
|
||||
|
||||
extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
unsigned int size);
|
||||
extern int usb_get_bos_descriptor(struct usb_device *dev);
|
||||
extern void usb_release_bos_descriptor(struct usb_device *dev);
|
||||
extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
||||
extern int usb_choose_configuration(struct usb_device *udev);
|
||||
|
||||
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);
|
||||
|
||||
return c->desc.bMaxPower * mul;
|
||||
}
|
||||
|
||||
extern void usb_kick_hub_wq(struct usb_device *dev);
|
||||
extern int usb_match_one_id_intf(struct usb_device *dev,
|
||||
struct usb_host_interface *intf,
|
||||
const struct usb_device_id *id);
|
||||
extern int usb_match_device(struct usb_device *dev,
|
||||
const struct usb_device_id *id);
|
||||
extern void usb_forced_unbind_intf(struct usb_interface *intf);
|
||||
extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
|
||||
|
||||
extern void usb_hub_release_all_ports(struct usb_device *hdev,
|
||||
struct usb_dev_state *owner);
|
||||
extern bool usb_device_is_owned(struct usb_device *udev);
|
||||
|
||||
extern int usb_hub_init(void);
|
||||
extern void usb_hub_cleanup(void);
|
||||
extern int usb_major_init(void);
|
||||
extern void usb_major_cleanup(void);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
extern int usb_suspend(struct device *dev, pm_message_t msg);
|
||||
extern int usb_resume(struct device *dev, pm_message_t msg);
|
||||
extern int usb_resume_complete(struct device *dev);
|
||||
|
||||
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
|
||||
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
|
||||
|
||||
#else
|
||||
|
||||
static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
extern void usb_autosuspend_device(struct usb_device *udev);
|
||||
extern int usb_autoresume_device(struct usb_device *udev);
|
||||
extern int usb_remote_wakeup(struct usb_device *dev);
|
||||
extern int usb_runtime_suspend(struct device *dev);
|
||||
extern int usb_runtime_resume(struct device *dev);
|
||||
extern int usb_runtime_idle(struct device *dev);
|
||||
extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable);
|
||||
|
||||
#else
|
||||
|
||||
#define usb_autosuspend_device(udev) do {} while (0)
|
||||
static inline int usb_autoresume_device(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern struct bus_type usb_bus_type;
|
||||
extern struct mutex usb_port_peer_mutex;
|
||||
extern struct device_type usb_device_type;
|
||||
extern struct device_type usb_if_device_type;
|
||||
extern struct device_type usb_ep_device_type;
|
||||
extern struct device_type usb_port_device_type;
|
||||
extern struct usb_device_driver usb_generic_driver;
|
||||
|
||||
static inline int is_usb_device(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb_device_type;
|
||||
}
|
||||
|
||||
static inline int is_usb_interface(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb_if_device_type;
|
||||
}
|
||||
|
||||
static inline int is_usb_endpoint(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb_ep_device_type;
|
||||
}
|
||||
|
||||
static inline int is_usb_port(const struct device *dev)
|
||||
{
|
||||
return dev->type == &usb_port_device_type;
|
||||
}
|
||||
|
||||
/* Do the same for device drivers and interface drivers. */
|
||||
|
||||
static inline int is_usb_device_driver(struct device_driver *drv)
|
||||
{
|
||||
return container_of(drv, struct usbdrv_wrap, driver)->
|
||||
for_devices;
|
||||
}
|
||||
|
||||
/* for labeling diagnostics */
|
||||
extern const char *usbcore_name;
|
||||
|
||||
/* sysfs stuff */
|
||||
extern const struct attribute_group *usb_device_groups[];
|
||||
extern const struct attribute_group *usb_interface_groups[];
|
||||
|
||||
/* usbfs stuff */
|
||||
extern struct mutex usbfs_mutex;
|
||||
extern struct usb_driver usbfs_driver;
|
||||
extern const struct file_operations usbfs_devices_fops;
|
||||
extern const struct file_operations usbdev_file_operations;
|
||||
extern void usbfs_conn_disc_event(void);
|
||||
|
||||
extern int usb_devio_init(void);
|
||||
extern void usb_devio_cleanup(void);
|
||||
|
||||
/*
|
||||
* Firmware specific cookie identifying a port's location. '0' == no location
|
||||
* data available
|
||||
*/
|
||||
typedef u32 usb_port_location_t;
|
||||
|
||||
/* internal notify stuff */
|
||||
extern void usb_notify_add_device(struct usb_device *udev);
|
||||
extern void usb_notify_remove_device(struct usb_device *udev);
|
||||
extern void usb_notify_add_bus(struct usb_bus *ubus);
|
||||
extern void usb_notify_remove_bus(struct usb_bus *ubus);
|
||||
extern void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
|
||||
struct usb_hub_descriptor *desc);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int usb_acpi_register(void);
|
||||
extern void usb_acpi_unregister(void);
|
||||
extern acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
|
||||
int port1);
|
||||
#else
|
||||
static inline int usb_acpi_register(void) { return 0; };
|
||||
static inline void usb_acpi_unregister(void) { };
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue