Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
92
drivers/usb/core/Kconfig
Normal file
92
drivers/usb/core/Kconfig
Normal file
@@ -0,0 +1,92 @@
|
||||
#
|
||||
# USB Core configuration
|
||||
#
|
||||
config USB_DEBUG
|
||||
bool "USB verbose debug messages"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want the USB core & hub drivers to produce a bunch
|
||||
of debug messages to the system log. Select this if you are having a
|
||||
problem with USB support and want to see more of what is going on.
|
||||
|
||||
comment "Miscellaneous USB options"
|
||||
depends on USB
|
||||
|
||||
config USB_DEVICEFS
|
||||
bool "USB device filesystem"
|
||||
depends on USB
|
||||
---help---
|
||||
If you say Y here (and to "/proc file system support" in the "File
|
||||
systems" section, above), you will get a file /proc/bus/usb/devices
|
||||
which lists the devices currently connected to your USB bus or
|
||||
busses, and for every connected device a file named
|
||||
"/proc/bus/usb/xxx/yyy", where xxx is the bus number and yyy the
|
||||
device number; the latter files can be used by user space programs
|
||||
to talk directly to the device. These files are "virtual", meaning
|
||||
they are generated on the fly and not stored on the hard drive.
|
||||
|
||||
You may need to mount the usbfs file system to see the files, use
|
||||
mount -t usbfs none /proc/bus/usb
|
||||
|
||||
For the format of the various /proc/bus/usb/ files, please read
|
||||
<file:Documentation/usb/proc_usb_info.txt>.
|
||||
|
||||
Most users want to say Y here.
|
||||
|
||||
config USB_DYNAMIC_MINORS
|
||||
bool "Dynamic USB minor allocation (EXPERIMENTAL)"
|
||||
depends on USB && EXPERIMENTAL
|
||||
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_SUSPEND
|
||||
bool "USB selective suspend/resume and wakeup (EXPERIMENTAL)"
|
||||
depends on USB && PM && EXPERIMENTAL
|
||||
help
|
||||
If you say Y here, you can use driver calls or the sysfs
|
||||
"power/state" file to suspend or resume individual USB
|
||||
peripherals.
|
||||
|
||||
Also, USB "remote wakeup" signaling is supported, whereby some
|
||||
USB devices (like keyboards and network adapters) can wake up
|
||||
their parent hub. That wakeup cascades up the USB tree, and
|
||||
could wake the system from states like suspend-to-RAM.
|
||||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
config USB_OTG
|
||||
bool
|
||||
depends on USB && EXPERIMENTAL
|
||||
select USB_SUSPEND
|
||||
#default n
|
||||
|
||||
|
||||
config USB_OTG_WHITELIST
|
||||
bool "Rely on OTG Targeted Peripherals List"
|
||||
depends on USB_OTG
|
||||
default y
|
||||
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 specification for all devices not on your product's
|
||||
"Targeted Peripherals List".
|
||||
|
||||
Otherwise, peripherals not listed there will only generate a
|
||||
warning and enumeration will continue. That's more like what
|
||||
normal Linux-USB hosts do (other than the warning), and is
|
||||
convenient for many stages of product development.
|
||||
|
||||
config USB_OTG_BLACKLIST_HUB
|
||||
bool "Disable external hubs"
|
||||
depends on USB_OTG
|
||||
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.
|
||||
|
||||
21
drivers/usb/core/Makefile
Normal file
21
drivers/usb/core/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# Makefile for USB Core files and filesystem
|
||||
#
|
||||
|
||||
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
|
||||
config.o file.o buffer.o sysfs.o endpoint.o \
|
||||
devio.o notify.o generic.o quirks.o
|
||||
|
||||
ifeq ($(CONFIG_PCI),y)
|
||||
usbcore-objs += hcd-pci.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USB_DEVICEFS),y)
|
||||
usbcore-objs += inode.o devices.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB) += usbcore.o
|
||||
|
||||
ifeq ($(CONFIG_USB_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
148
drivers/usb/core/buffer.c
Normal file
148
drivers/usb/core/buffer.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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, regardles of bus type.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hcd.h"
|
||||
|
||||
|
||||
/*
|
||||
* DMA-Coherent Buffers
|
||||
*/
|
||||
|
||||
/* FIXME tune these based on pool statistics ... */
|
||||
static const size_t pool_max [HCD_BUFFER_POOLS] = {
|
||||
/* platforms without dma-friendly caches might need to
|
||||
* prevent cacheline sharing...
|
||||
*/
|
||||
32,
|
||||
128,
|
||||
512,
|
||||
PAGE_SIZE / 2
|
||||
/* bigger --> allocate pages */
|
||||
};
|
||||
|
||||
|
||||
/* 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, or returns a negative
|
||||
* errno value on error.
|
||||
*
|
||||
* Call hcd_buffer_destroy() to clean up after using those pools.
|
||||
*/
|
||||
int hcd_buffer_create(struct usb_hcd *hcd)
|
||||
{
|
||||
char name[16];
|
||||
int i, size;
|
||||
|
||||
if (!hcd->self.controller->dma_mask)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
if (!(size = pool_max [i]))
|
||||
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) {
|
||||
*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, 0);
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
529
drivers/usb/core/config.c
Normal file
529
drivers/usb/core/config.c
Normal file
@@ -0,0 +1,529 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
#define USB_MAXALTSETTING 128 /* Hard limit */
|
||||
#define USB_MAXENDPOINTS 30 /* 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 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;
|
||||
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
if (n > 0)
|
||||
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
||||
n, plural(n), "endpoint");
|
||||
return buffer - buffer0 + i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 device *ddev, int cfgidx,
|
||||
struct usb_host_config *config, unsigned char *buffer, int size)
|
||||
{
|
||||
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];
|
||||
|
||||
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
|
||||
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
|
||||
config->desc.bLength < USB_DT_CONFIG_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 (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_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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
// 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 = -ENOMEM;
|
||||
unsigned int cfgno, length;
|
||||
unsigned char *buffer;
|
||||
unsigned char *bigbuffer;
|
||||
struct usb_config_descriptor *desc;
|
||||
|
||||
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;
|
||||
|
||||
buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
goto err2;
|
||||
desc = (struct usb_config_descriptor *)buffer;
|
||||
|
||||
for (cfgno = 0; 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,
|
||||
buffer, USB_DT_CONFIG_SIZE);
|
||||
if (result < 0) {
|
||||
dev_err(ddev, "unable to read config index %d "
|
||||
"descriptor/%s\n", cfgno, "start");
|
||||
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;
|
||||
}
|
||||
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->dev, cfgno,
|
||||
&dev->config[cfgno], bigbuffer, length);
|
||||
if (result < 0) {
|
||||
++cfgno;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
result = 0;
|
||||
|
||||
err:
|
||||
kfree(buffer);
|
||||
dev->descriptor.bNumConfigurations = cfgno;
|
||||
err2:
|
||||
if (result == -ENOMEM)
|
||||
dev_err(ddev, "out of memory\n");
|
||||
return result;
|
||||
}
|
||||
682
drivers/usb/core/devices.c
Normal file
682
drivers/usb/core/devices.c
Normal file
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* $Id: devices.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
#define MAX_TOPO_LEVEL 6
|
||||
|
||||
/* 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=ddd MxCh=dd */
|
||||
"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s 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_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.
|
||||
*/
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq);
|
||||
static unsigned int conndiscevcnt = 0;
|
||||
|
||||
/* this struct stores the poll state for <mountpoint>/devices pollers */
|
||||
struct usb_device_status {
|
||||
unsigned int lastev;
|
||||
};
|
||||
|
||||
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_HUB, "hub"},
|
||||
{USB_CLASS_PHYSICAL, "PID"},
|
||||
{USB_CLASS_PRINTER, "print"},
|
||||
{USB_CLASS_MASS_STORAGE, "stor."},
|
||||
{USB_CLASS_CDC_DATA, "data"},
|
||||
{USB_CLASS_APP_SPEC, "app."},
|
||||
{USB_CLASS_VENDOR_SPEC, "vend."},
|
||||
{USB_CLASS_STILL_IMAGE, "still"},
|
||||
{USB_CLASS_CSCID, "scard"},
|
||||
{USB_CLASS_CONTENT_SEC, "c-sec"},
|
||||
{-1, "unk."} /* leave as last */
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
void usbfs_conn_disc_event(void)
|
||||
{
|
||||
conndiscevcnt++;
|
||||
wake_up(&deviceconndiscwq);
|
||||
}
|
||||
|
||||
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 (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) {
|
||||
case 1 << 11: bandwidth = 2; break;
|
||||
case 2 << 11: bandwidth = 3; break;
|
||||
}
|
||||
}
|
||||
|
||||
/* this isn't checking for illegal values */
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
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)
|
||||
interval = 1 << (desc->bInterval - 1);
|
||||
else
|
||||
interval = desc->bInterval;
|
||||
break;
|
||||
default: /* "can't happen" */
|
||||
return start;
|
||||
}
|
||||
interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000;
|
||||
if (interval % 1000)
|
||||
unit = 'u';
|
||||
else {
|
||||
unit = 'm';
|
||||
interval /= 1000;
|
||||
}
|
||||
|
||||
start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
|
||||
desc->bmAttributes, type,
|
||||
(le16_to_cpu(desc->wMaxPacketSize) & 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 = &intfc->altsetting[setno].desc;
|
||||
const char *driver_name = "";
|
||||
int active = 0;
|
||||
|
||||
if (start > end)
|
||||
return start;
|
||||
down_read(&usb_bus_type.subsys.rwsem);
|
||||
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);
|
||||
up_read(&usb_bus_type.subsys.rwsem);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if (start > end)
|
||||
return start;
|
||||
start += sprintf(start, format_config,
|
||||
active ? '*' : ' ', /* mark active/actual/current cfg. */
|
||||
desc->bNumInterfaces,
|
||||
desc->bConfigurationValue,
|
||||
desc->bmAttributes,
|
||||
desc->bMaxPower * 2);
|
||||
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);
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
if (!(pages_start = (char*) __get_free_pages(GFP_KERNEL,1)))
|
||||
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_HIGH:
|
||||
speed = "480"; 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;
|
||||
|
||||
/* high speed reserves 80%, full/low reserves 90% */
|
||||
if (usbdev->speed == USB_SPEED_HIGH)
|
||||
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. */
|
||||
for (chix = 0; chix < usbdev->maxchild; chix++) {
|
||||
struct usb_device *childdev = usbdev->children[chix];
|
||||
|
||||
if (childdev) {
|
||||
usb_lock_device(childdev);
|
||||
ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev,
|
||||
bus, level + 1, chix, ++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->root_hub)
|
||||
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)
|
||||
{
|
||||
struct usb_device_status *st = file->private_data;
|
||||
unsigned int mask = 0;
|
||||
|
||||
lock_kernel();
|
||||
if (!st) {
|
||||
st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
|
||||
|
||||
/* we may have dropped BKL - need to check for having lost the race */
|
||||
if (file->private_data) {
|
||||
kfree(st);
|
||||
st = file->private_data;
|
||||
goto lost_race;
|
||||
}
|
||||
/* we haven't lost - check for allocation failure now */
|
||||
if (!st) {
|
||||
unlock_kernel();
|
||||
return POLLIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* need to prevent the module from being unloaded, since
|
||||
* proc_unregister does not call the release method and
|
||||
* we would have a memory leak
|
||||
*/
|
||||
st->lastev = conndiscevcnt;
|
||||
file->private_data = st;
|
||||
mask = POLLIN;
|
||||
}
|
||||
lost_race:
|
||||
if (file->f_mode & FMODE_READ)
|
||||
poll_wait(file, &deviceconndiscwq, wait);
|
||||
if (st->lastev != conndiscevcnt)
|
||||
mask |= POLLIN;
|
||||
st->lastev = conndiscevcnt;
|
||||
unlock_kernel();
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int usb_device_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_device_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
kfree(file->private_data);
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
|
||||
{
|
||||
loff_t ret;
|
||||
|
||||
lock_kernel();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct file_operations usbfs_devices_fops = {
|
||||
.llseek = usb_device_lseek,
|
||||
.read = usb_device_read,
|
||||
.poll = usb_device_poll,
|
||||
.open = usb_device_open,
|
||||
.release = usb_device_release,
|
||||
};
|
||||
1682
drivers/usb/core/devio.c
Normal file
1682
drivers/usb/core/devio.c
Normal file
File diff suppressed because it is too large
Load Diff
1454
drivers/usb/core/driver.c
Normal file
1454
drivers/usb/core/driver.c
Normal file
File diff suppressed because it is too large
Load Diff
357
drivers/usb/core/endpoint.c
Normal file
357
drivers/usb/core/endpoint.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* 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/idr.h>
|
||||
#include <linux/usb.h>
|
||||
#include "usb.h"
|
||||
|
||||
#define MAX_ENDPOINT_MINORS (64*128*32)
|
||||
static int usb_endpoint_major;
|
||||
static DEFINE_IDR(endpoint_idr);
|
||||
|
||||
struct ep_device {
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
struct usb_device *udev;
|
||||
struct device dev;
|
||||
int minor;
|
||||
};
|
||||
#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 show_ep_##field(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(field, S_IRUGO, show_ep_##field, NULL);
|
||||
|
||||
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 show_ep_wMaxPacketSize(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ep_device *ep = to_ep_device(dev);
|
||||
return sprintf(buf, "%04x\n",
|
||||
le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
|
||||
}
|
||||
static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
|
||||
|
||||
static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ep_device *ep = to_ep_device(dev);
|
||||
char *type = "unknown";
|
||||
|
||||
switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
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(type, S_IRUGO, show_ep_type, NULL);
|
||||
|
||||
static ssize_t show_ep_interval(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 (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
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(interval, S_IRUGO, show_ep_interval, NULL);
|
||||
|
||||
static ssize_t show_ep_direction(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ep_device *ep = to_ep_device(dev);
|
||||
char *direction;
|
||||
|
||||
if ((ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
|
||||
USB_ENDPOINT_XFER_CONTROL)
|
||||
direction = "both";
|
||||
else if (ep->desc->bEndpointAddress & USB_DIR_IN)
|
||||
direction = "in";
|
||||
else
|
||||
direction = "out";
|
||||
return sprintf(buf, "%s\n", direction);
|
||||
}
|
||||
static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL);
|
||||
|
||||
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 int usb_endpoint_major_init(void)
|
||||
{
|
||||
dev_t dev;
|
||||
int error;
|
||||
|
||||
error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS,
|
||||
"usb_endpoint");
|
||||
if (error) {
|
||||
err("unable to get a dynamic major for usb endpoints");
|
||||
return error;
|
||||
}
|
||||
usb_endpoint_major = MAJOR(dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_endpoint_major_cleanup(void)
|
||||
{
|
||||
unregister_chrdev_region(MKDEV(usb_endpoint_major, 0),
|
||||
MAX_ENDPOINT_MINORS);
|
||||
}
|
||||
|
||||
static int endpoint_get_minor(struct ep_device *ep_dev)
|
||||
{
|
||||
static DEFINE_MUTEX(minor_lock);
|
||||
int retval = -ENOMEM;
|
||||
int id;
|
||||
|
||||
mutex_lock(&minor_lock);
|
||||
if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0)
|
||||
goto exit;
|
||||
|
||||
retval = idr_get_new(&endpoint_idr, ep_dev, &id);
|
||||
if (retval < 0) {
|
||||
if (retval == -EAGAIN)
|
||||
retval = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
ep_dev->minor = id & MAX_ID_MASK;
|
||||
exit:
|
||||
mutex_unlock(&minor_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void endpoint_free_minor(struct ep_device *ep_dev)
|
||||
{
|
||||
idr_remove(&endpoint_idr, ep_dev->minor);
|
||||
}
|
||||
|
||||
static struct endpoint_class {
|
||||
struct kref kref;
|
||||
struct class *class;
|
||||
} *ep_class;
|
||||
|
||||
static int init_endpoint_class(void)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (ep_class != NULL) {
|
||||
kref_get(&ep_class->kref);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL);
|
||||
if (!ep_class) {
|
||||
result = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
kref_init(&ep_class->kref);
|
||||
ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
|
||||
if (IS_ERR(ep_class->class)) {
|
||||
result = PTR_ERR(ep_class->class);
|
||||
goto class_create_error;
|
||||
}
|
||||
|
||||
result = usb_endpoint_major_init();
|
||||
if (result)
|
||||
goto endpoint_major_error;
|
||||
|
||||
goto exit;
|
||||
|
||||
endpoint_major_error:
|
||||
class_destroy(ep_class->class);
|
||||
class_create_error:
|
||||
kfree(ep_class);
|
||||
ep_class = NULL;
|
||||
exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void release_endpoint_class(struct kref *kref)
|
||||
{
|
||||
/* Ok, we cheat as we know we only have one ep_class */
|
||||
class_destroy(ep_class->class);
|
||||
kfree(ep_class);
|
||||
ep_class = NULL;
|
||||
usb_endpoint_major_cleanup();
|
||||
}
|
||||
|
||||
static void destroy_endpoint_class(void)
|
||||
{
|
||||
if (ep_class)
|
||||
kref_put(&ep_class->kref, release_endpoint_class);
|
||||
}
|
||||
|
||||
static void ep_device_release(struct device *dev)
|
||||
{
|
||||
struct ep_device *ep_dev = to_ep_device(dev);
|
||||
|
||||
dev_dbg(dev, "%s called for %s\n", __FUNCTION__, dev->bus_id);
|
||||
endpoint_free_minor(ep_dev);
|
||||
kfree(ep_dev);
|
||||
}
|
||||
|
||||
int usb_create_ep_files(struct device *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
char name[8];
|
||||
struct ep_device *ep_dev;
|
||||
int retval;
|
||||
|
||||
retval = init_endpoint_class();
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
|
||||
if (!ep_dev) {
|
||||
retval = -ENOMEM;
|
||||
goto error_alloc;
|
||||
}
|
||||
|
||||
retval = endpoint_get_minor(ep_dev);
|
||||
if (retval) {
|
||||
dev_err(parent, "can not allocate minor number for %s",
|
||||
ep_dev->dev.bus_id);
|
||||
goto error_register;
|
||||
}
|
||||
|
||||
ep_dev->desc = &endpoint->desc;
|
||||
ep_dev->udev = udev;
|
||||
ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor);
|
||||
ep_dev->dev.class = ep_class->class;
|
||||
ep_dev->dev.parent = parent;
|
||||
ep_dev->dev.release = ep_device_release;
|
||||
snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x",
|
||||
udev->bus->busnum, udev->devnum,
|
||||
endpoint->desc.bEndpointAddress);
|
||||
|
||||
retval = device_register(&ep_dev->dev);
|
||||
if (retval)
|
||||
goto error_chrdev;
|
||||
retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
|
||||
if (retval)
|
||||
goto error_group;
|
||||
|
||||
/* create the symlink to the old-style "ep_XX" directory */
|
||||
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name);
|
||||
if (retval)
|
||||
goto error_link;
|
||||
endpoint->ep_dev = ep_dev;
|
||||
return retval;
|
||||
|
||||
error_link:
|
||||
sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
|
||||
error_group:
|
||||
device_unregister(&ep_dev->dev);
|
||||
destroy_endpoint_class();
|
||||
return retval;
|
||||
|
||||
error_chrdev:
|
||||
endpoint_free_minor(ep_dev);
|
||||
|
||||
error_register:
|
||||
kfree(ep_dev);
|
||||
error_alloc:
|
||||
destroy_endpoint_class();
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
|
||||
{
|
||||
struct ep_device *ep_dev = endpoint->ep_dev;
|
||||
|
||||
if (ep_dev) {
|
||||
char name[8];
|
||||
|
||||
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
|
||||
sysfs_remove_link(&ep_dev->dev.parent->kobj, name);
|
||||
sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
|
||||
device_unregister(&ep_dev->dev);
|
||||
endpoint->ep_dev = NULL;
|
||||
destroy_endpoint_class();
|
||||
}
|
||||
}
|
||||
251
drivers/usb/core/file.c
Normal file
251
drivers/usb/core/file.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
#define MAX_USB_MINORS 256
|
||||
static const struct file_operations *usb_minors[MAX_USB_MINORS];
|
||||
static DEFINE_SPINLOCK(minor_lock);
|
||||
|
||||
static int usb_open(struct inode * inode, struct file * file)
|
||||
{
|
||||
int minor = iminor(inode);
|
||||
const struct file_operations *c;
|
||||
int err = -ENODEV;
|
||||
const struct file_operations *old_fops, *new_fops = NULL;
|
||||
|
||||
spin_lock (&minor_lock);
|
||||
c = usb_minors[minor];
|
||||
|
||||
if (!c || !(new_fops = fops_get(c))) {
|
||||
spin_unlock(&minor_lock);
|
||||
return err;
|
||||
}
|
||||
spin_unlock(&minor_lock);
|
||||
|
||||
old_fops = file->f_op;
|
||||
file->f_op = new_fops;
|
||||
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
|
||||
if (file->f_op->open)
|
||||
err = file->f_op->open(inode,file);
|
||||
if (err) {
|
||||
fops_put(file->f_op);
|
||||
file->f_op = fops_get(old_fops);
|
||||
}
|
||||
fops_put(old_fops);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct file_operations usb_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = usb_open,
|
||||
};
|
||||
|
||||
static struct usb_class {
|
||||
struct kref kref;
|
||||
struct class *class;
|
||||
} *usb_class;
|
||||
|
||||
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, "usb");
|
||||
if (IS_ERR(usb_class->class)) {
|
||||
result = IS_ERR(usb_class->class);
|
||||
err("class_create failed for usb devices");
|
||||
kfree(usb_class);
|
||||
usb_class = NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
err("unable to get major %d for usb devices", 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.
|
||||
*
|
||||
* Returns -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 = -EINVAL;
|
||||
int minor_base = class_driver->minor_base;
|
||||
int minor = 0;
|
||||
char name[BUS_ID_SIZE];
|
||||
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
|
||||
intf->minor = -1;
|
||||
|
||||
dbg ("looking for a minor, starting at %d", minor_base);
|
||||
|
||||
if (class_driver->fops == NULL)
|
||||
goto exit;
|
||||
|
||||
spin_lock (&minor_lock);
|
||||
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
|
||||
if (usb_minors[minor])
|
||||
continue;
|
||||
|
||||
usb_minors[minor] = class_driver->fops;
|
||||
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
spin_unlock (&minor_lock);
|
||||
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
retval = init_usb_class();
|
||||
if (retval)
|
||||
goto exit;
|
||||
|
||||
intf->minor = minor;
|
||||
|
||||
/* create a usb class device for this usb interface */
|
||||
snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
|
||||
temp = strrchr(name, '/');
|
||||
if (temp && (temp[1] != 0x00))
|
||||
++temp;
|
||||
else
|
||||
temp = name;
|
||||
intf->usb_dev = device_create(usb_class->class, &intf->dev,
|
||||
MKDEV(USB_MAJOR, minor), "%s", temp);
|
||||
if (IS_ERR(intf->usb_dev)) {
|
||||
spin_lock (&minor_lock);
|
||||
usb_minors[intf->minor] = NULL;
|
||||
spin_unlock (&minor_lock);
|
||||
retval = PTR_ERR(intf->usb_dev);
|
||||
}
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(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)
|
||||
{
|
||||
int minor_base = class_driver->minor_base;
|
||||
char name[BUS_ID_SIZE];
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
minor_base = 0;
|
||||
#endif
|
||||
|
||||
if (intf->minor == -1)
|
||||
return;
|
||||
|
||||
dbg ("removing %d minor", intf->minor);
|
||||
|
||||
spin_lock (&minor_lock);
|
||||
usb_minors[intf->minor] = NULL;
|
||||
spin_unlock (&minor_lock);
|
||||
|
||||
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
|
||||
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
|
||||
intf->usb_dev = NULL;
|
||||
intf->minor = -1;
|
||||
destroy_usb_class();
|
||||
}
|
||||
EXPORT_SYMBOL(usb_deregister_dev);
|
||||
|
||||
|
||||
219
drivers/usb/core/generic.c
Normal file
219
drivers/usb/core/generic.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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 "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;
|
||||
}
|
||||
|
||||
static int choose_configuration(struct usb_device *udev)
|
||||
{
|
||||
int i;
|
||||
int num_configs;
|
||||
int insufficient_power = 0;
|
||||
struct usb_host_config *c, *best;
|
||||
|
||||
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 (c->desc.bMaxPower * 2 > 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.
|
||||
*/
|
||||
if (i == 0 && 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_info(&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;
|
||||
}
|
||||
|
||||
static int generic_probe(struct usb_device *udev)
|
||||
{
|
||||
int err, c;
|
||||
|
||||
/* put device-specific files into sysfs */
|
||||
usb_create_sysfs_dev_files(udev);
|
||||
|
||||
/* Choose and set the configuration. This registers the interfaces
|
||||
* with the driver core and lets interface drivers bind to them.
|
||||
*/
|
||||
c = choose_configuration(udev);
|
||||
if (c >= 0) {
|
||||
err = usb_set_configuration(udev, c);
|
||||
if (err) {
|
||||
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);
|
||||
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int generic_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
/* USB devices enter SUSPEND state through their hubs, but can be
|
||||
* marked for FREEZE as soon as their children are already idled.
|
||||
* But those semantics are useless, so we equate the two (sigh).
|
||||
*/
|
||||
return usb_port_suspend(udev);
|
||||
}
|
||||
|
||||
static int generic_resume(struct usb_device *udev)
|
||||
{
|
||||
return usb_port_resume(udev);
|
||||
}
|
||||
|
||||
#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,
|
||||
};
|
||||
432
drivers/usb/core/hcd-pci.c
Normal file
432
drivers/usb/core/hcd-pci.c
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* (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 <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"
|
||||
#include "hcd.h"
|
||||
|
||||
|
||||
/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* 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().
|
||||
*/
|
||||
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;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
if (!id || !(driver = (struct hc_driver *) id->driver_data))
|
||||
return -EINVAL;
|
||||
|
||||
if (pci_enable_device (dev) < 0)
|
||||
return -ENODEV;
|
||||
dev->current_state = PCI_D0;
|
||||
dev->dev.power.power_state = PMSG_ON;
|
||||
|
||||
if (!dev->irq) {
|
||||
dev_err (&dev->dev,
|
||||
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
|
||||
pci_name(dev));
|
||||
retval = -ENODEV;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd (driver, &dev->dev, pci_name(dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
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 err2;
|
||||
}
|
||||
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 err3;
|
||||
}
|
||||
|
||||
} 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 err1;
|
||||
}
|
||||
}
|
||||
|
||||
pci_set_master (dev);
|
||||
|
||||
retval = usb_add_hcd (hcd, dev->irq, IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
goto err4;
|
||||
return retval;
|
||||
|
||||
err4:
|
||||
if (driver->flags & HCD_MEMORY) {
|
||||
iounmap (hcd->regs);
|
||||
err3:
|
||||
release_mem_region (hcd->rsrc_start, hcd->rsrc_len);
|
||||
} else
|
||||
release_region (hcd->rsrc_start, hcd->rsrc_len);
|
||||
err2:
|
||||
usb_put_hcd (hcd);
|
||||
err1:
|
||||
pci_disable_device (dev);
|
||||
dev_err (&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL (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;
|
||||
|
||||
usb_remove_hcd (hcd);
|
||||
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 (usb_hcd_pci_remove);
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
|
||||
* @dev: USB Host Controller being suspended
|
||||
* @message: semantics in flux
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as suspend().
|
||||
*/
|
||||
int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int retval = 0;
|
||||
int has_pci_pm;
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
|
||||
/* 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...
|
||||
*
|
||||
* We must ignore the FREEZE vs SUSPEND distinction here, because
|
||||
* otherwise the swsusp will save (and restore) garbage state.
|
||||
*/
|
||||
if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON)
|
||||
return -EBUSY;
|
||||
|
||||
if (hcd->driver->suspend) {
|
||||
retval = hcd->driver->suspend(hcd, message);
|
||||
suspend_report_result(hcd->driver->suspend, retval);
|
||||
if (retval)
|
||||
goto done;
|
||||
}
|
||||
synchronize_irq(dev->irq);
|
||||
|
||||
/* FIXME until the generic PM interfaces change a lot more, this
|
||||
* can't use PCI D1 and D2 states. For example, the confusion
|
||||
* between messages and states will need to vanish, and messages
|
||||
* will need to provide a target system state again.
|
||||
*
|
||||
* It'll be important to learn characteristics of the target state,
|
||||
* especially on embedded hardware where the HCD will often be in
|
||||
* charge of an external VBUS power supply and one or more clocks.
|
||||
* Some target system states will leave them active; others won't.
|
||||
* (With PCI, that's often handled by platform BIOS code.)
|
||||
*/
|
||||
|
||||
/* even when the PCI layer rejects some of the PCI calls
|
||||
* below, HCs can try global suspend and reduce DMA traffic.
|
||||
* PM-sensitive HCDs may already have done this.
|
||||
*/
|
||||
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
|
||||
/* 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) and enter some PCI
|
||||
* low power state, if the hardware allows.
|
||||
*/
|
||||
if (hcd->state == HC_STATE_SUSPENDED) {
|
||||
|
||||
/* no DMA or IRQs except when HC is active */
|
||||
if (dev->current_state == PCI_D0) {
|
||||
pci_save_state (dev);
|
||||
pci_disable_device (dev);
|
||||
}
|
||||
|
||||
if (!has_pci_pm) {
|
||||
dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* NOTE: dev->current_state becomes nonzero only here, and
|
||||
* only for devices that support PCI PM. Also, exiting
|
||||
* PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset
|
||||
* some device state (e.g. as part of clock reinit).
|
||||
*/
|
||||
retval = pci_set_power_state (dev, PCI_D3hot);
|
||||
suspend_report_result(pci_set_power_state, retval);
|
||||
if (retval == 0) {
|
||||
int wake = device_can_wakeup(&hcd->self.root_hub->dev);
|
||||
|
||||
wake = wake && device_may_wakeup(hcd->self.controller);
|
||||
|
||||
dev_dbg (hcd->self.controller, "--> PCI D3%s\n",
|
||||
wake ? "/wakeup" : "");
|
||||
|
||||
/* Ignore these return values. We rely on pci code to
|
||||
* reject requests the hardware can't implement, rather
|
||||
* than coding the same thing.
|
||||
*/
|
||||
(void) pci_enable_wake (dev, PCI_D3hot, wake);
|
||||
(void) pci_enable_wake (dev, PCI_D3cold, wake);
|
||||
} else {
|
||||
dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n",
|
||||
retval);
|
||||
(void) usb_hcd_pci_resume (dev);
|
||||
}
|
||||
|
||||
} else if (hcd->state != HC_STATE_HALT) {
|
||||
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
|
||||
hcd->state);
|
||||
WARN_ON(1);
|
||||
retval = -EINVAL;
|
||||
}
|
||||
|
||||
done:
|
||||
if (retval == 0) {
|
||||
dev->dev.power.power_state = PMSG_SUSPEND;
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Disable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node (dev);
|
||||
if (of_node)
|
||||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL (usb_hcd_pci_suspend);
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
|
||||
* @dev: USB Host Controller being resumed
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as resume().
|
||||
*/
|
||||
int usb_hcd_pci_resume (struct pci_dev *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int retval;
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
dev_dbg (hcd->self.controller,
|
||||
"can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Reenable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
||||
of_node = pci_device_to_OF_node (dev);
|
||||
if (of_node)
|
||||
pmac_call_feature (PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* NOTE: chip docs cover clean "real suspend" cases (what Linux
|
||||
* calls "standby", "suspend to RAM", and so on). There are also
|
||||
* dirty cases when swsusp fakes a suspend in "shutdown" mode.
|
||||
*/
|
||||
if (dev->current_state != PCI_D0) {
|
||||
#ifdef DEBUG
|
||||
int pci_pm;
|
||||
u16 pmcr;
|
||||
|
||||
pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
pci_read_config_word(dev, pci_pm + PCI_PM_CTRL, &pmcr);
|
||||
pmcr &= PCI_PM_CTRL_STATE_MASK;
|
||||
if (pmcr) {
|
||||
/* Clean case: power to USB and to HC registers was
|
||||
* maintained; remote wakeup is easy.
|
||||
*/
|
||||
dev_dbg(hcd->self.controller, "resume from PCI D%d\n",
|
||||
pmcr);
|
||||
} else {
|
||||
/* Clean: HC lost Vcc power, D0 uninitialized
|
||||
* + Vaux may have preserved port and transceiver
|
||||
* state ... for remote wakeup from D3cold
|
||||
* + or not; HCD must reinit + re-enumerate
|
||||
*
|
||||
* Dirty: D0 semi-initialized cases with swsusp
|
||||
* + after BIOS init
|
||||
* + after Linux init (HCD statically linked)
|
||||
*/
|
||||
dev_dbg(hcd->self.controller,
|
||||
"PCI D0, from previous PCI D%d\n",
|
||||
dev->current_state);
|
||||
}
|
||||
#endif
|
||||
/* yes, ignore these results too... */
|
||||
(void) pci_enable_wake (dev, dev->current_state, 0);
|
||||
(void) pci_enable_wake (dev, PCI_D3cold, 0);
|
||||
} else {
|
||||
/* Same basic cases: clean (powered/not), dirty */
|
||||
dev_dbg(hcd->self.controller, "PCI legacy resume\n");
|
||||
}
|
||||
|
||||
/* NOTE: the PCI API itself is asymmetric here. We don't need to
|
||||
* pci_set_power_state(PCI_D0) since that's part of re-enabling;
|
||||
* but that won't re-enable bus mastering. Yet pci_disable_device()
|
||||
* explicitly disables bus mastering...
|
||||
*/
|
||||
retval = pci_enable_device (dev);
|
||||
if (retval < 0) {
|
||||
dev_err (hcd->self.controller,
|
||||
"can't re-enable after resume, %d!\n", retval);
|
||||
return retval;
|
||||
}
|
||||
pci_set_master (dev);
|
||||
pci_restore_state (dev);
|
||||
|
||||
dev->dev.power.power_state = PMSG_ON;
|
||||
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
|
||||
if (hcd->driver->resume) {
|
||||
retval = hcd->driver->resume(hcd);
|
||||
if (retval) {
|
||||
dev_err (hcd->self.controller,
|
||||
"PCI post-resume error %d!\n", retval);
|
||||
usb_hc_died (hcd);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL (usb_hcd_pci_resume);
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/**
|
||||
* 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 (hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
EXPORT_SYMBOL (usb_hcd_pci_shutdown);
|
||||
|
||||
1734
drivers/usb/core/hcd.c
Normal file
1734
drivers/usb/core/hcd.c
Normal file
File diff suppressed because it is too large
Load Diff
465
drivers/usb/core/hcd.h
Normal file
465
drivers/usb/core/hcd.h
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2002 by David Brownell
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/* This file contains declarations of usbcore internals that are mostly
|
||||
* used or exposed by Host Controller Drivers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* USB Packet IDs (PIDs)
|
||||
*/
|
||||
#define USB_PID_UNDEF_0 0xf0
|
||||
#define USB_PID_OUT 0xe1
|
||||
#define USB_PID_ACK 0xd2
|
||||
#define USB_PID_DATA0 0xc3
|
||||
#define USB_PID_PING 0xb4 /* USB 2.0 */
|
||||
#define USB_PID_SOF 0xa5
|
||||
#define USB_PID_NYET 0x96 /* USB 2.0 */
|
||||
#define USB_PID_DATA2 0x87 /* USB 2.0 */
|
||||
#define USB_PID_SPLIT 0x78 /* USB 2.0 */
|
||||
#define USB_PID_IN 0x69
|
||||
#define USB_PID_NAK 0x5a
|
||||
#define USB_PID_DATA1 0x4b
|
||||
#define USB_PID_PREAMBLE 0x3c /* Token mode */
|
||||
#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
|
||||
#define USB_PID_SETUP 0x2d
|
||||
#define USB_PID_STALL 0x1e
|
||||
#define USB_PID_MDATA 0x0f /* USB 2.0 */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* USB Host Controller Driver (usb_hcd) framework
|
||||
*
|
||||
* Since "struct usb_bus" is so thin, you can't share much code in it.
|
||||
* This framework is a layer over that, and should be more sharable.
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
struct usb_hcd {
|
||||
|
||||
/*
|
||||
* housekeeping
|
||||
*/
|
||||
struct usb_bus self; /* hcd is-a bus */
|
||||
struct kref kref; /* reference counter */
|
||||
|
||||
const char *product_desc; /* product/vendor string */
|
||||
char irq_descr[24]; /* driver + bus # */
|
||||
|
||||
struct timer_list rh_timer; /* drives root-hub polling */
|
||||
struct urb *status_urb; /* the current status urb */
|
||||
|
||||
/*
|
||||
* hardware info/state
|
||||
*/
|
||||
const struct hc_driver *driver; /* hw-specific hooks */
|
||||
|
||||
/* Flags that need to be manipulated atomically */
|
||||
unsigned long flags;
|
||||
#define HCD_FLAG_HW_ACCESSIBLE 0x00000001
|
||||
#define HCD_FLAG_SAW_IRQ 0x00000002
|
||||
|
||||
unsigned rh_registered:1;/* is root hub registered? */
|
||||
|
||||
/* The next flag is a stopgap, to be removed when all the HCDs
|
||||
* support the new root-hub polling mechanism. */
|
||||
unsigned uses_new_polling:1;
|
||||
unsigned poll_rh:1; /* poll for rh status? */
|
||||
unsigned poll_pending:1; /* status has changed? */
|
||||
unsigned wireless:1; /* Wireless USB HCD */
|
||||
|
||||
int irq; /* irq allocated */
|
||||
void __iomem *regs; /* device memory/io */
|
||||
u64 rsrc_start; /* memory/io resource start */
|
||||
u64 rsrc_len; /* memory/io resource length */
|
||||
unsigned power_budget; /* in mA, 0 = no limit */
|
||||
|
||||
#define HCD_BUFFER_POOLS 4
|
||||
struct dma_pool *pool [HCD_BUFFER_POOLS];
|
||||
|
||||
int state;
|
||||
# define __ACTIVE 0x01
|
||||
# define __SUSPEND 0x04
|
||||
# define __TRANSIENT 0x80
|
||||
|
||||
# define HC_STATE_HALT 0
|
||||
# define HC_STATE_RUNNING (__ACTIVE)
|
||||
# define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
|
||||
# define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT)
|
||||
# define HC_STATE_SUSPENDED (__SUSPEND)
|
||||
|
||||
#define HC_IS_RUNNING(state) ((state) & __ACTIVE)
|
||||
#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
|
||||
|
||||
/* more shared queuing code would be good; it should support
|
||||
* smarter scheduling, handle transaction translators, etc;
|
||||
* input size of periodic table to an interrupt scheduler.
|
||||
* (ohci 32, uhci 1024, ehci 256/512/1024).
|
||||
*/
|
||||
|
||||
/* The HC driver's private data is stored at the end of
|
||||
* this structure.
|
||||
*/
|
||||
unsigned long hcd_priv[0]
|
||||
__attribute__ ((aligned (sizeof(unsigned long))));
|
||||
};
|
||||
|
||||
/* 2.4 does this a bit differently ... */
|
||||
static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
|
||||
{
|
||||
return &hcd->self;
|
||||
}
|
||||
|
||||
static inline struct usb_hcd *bus_to_hcd (struct usb_bus *bus)
|
||||
{
|
||||
return container_of(bus, struct usb_hcd, self);
|
||||
}
|
||||
|
||||
struct hcd_timeout { /* timeouts we allocate */
|
||||
struct list_head timeout_list;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
struct hc_driver {
|
||||
const char *description; /* "ehci-hcd" etc */
|
||||
const char *product_desc; /* product/vendor string */
|
||||
size_t hcd_priv_size; /* size of private data */
|
||||
|
||||
/* irq handler */
|
||||
irqreturn_t (*irq) (struct usb_hcd *hcd);
|
||||
|
||||
int flags;
|
||||
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
|
||||
#define HCD_USB11 0x0010 /* USB 1.1 */
|
||||
#define HCD_USB2 0x0020 /* USB 2.0 */
|
||||
|
||||
/* called to init HCD and root hub */
|
||||
int (*reset) (struct usb_hcd *hcd);
|
||||
int (*start) (struct usb_hcd *hcd);
|
||||
|
||||
/* NOTE: these suspend/resume calls relate to the HC as
|
||||
* a whole, not just the root hub; they're for PCI bus glue.
|
||||
*/
|
||||
/* called after suspending the hub, before entering D3 etc */
|
||||
int (*suspend) (struct usb_hcd *hcd, pm_message_t message);
|
||||
|
||||
/* called after entering D0 (etc), before resuming the hub */
|
||||
int (*resume) (struct usb_hcd *hcd);
|
||||
|
||||
/* cleanly make HCD stop writing memory and doing I/O */
|
||||
void (*stop) (struct usb_hcd *hcd);
|
||||
|
||||
/* shutdown HCD */
|
||||
void (*shutdown) (struct usb_hcd *hcd);
|
||||
|
||||
/* return current frame number */
|
||||
int (*get_frame_number) (struct usb_hcd *hcd);
|
||||
|
||||
/* manage i/o requests, device state */
|
||||
int (*urb_enqueue) (struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep,
|
||||
struct urb *urb,
|
||||
gfp_t mem_flags);
|
||||
int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
|
||||
|
||||
/* hw synch, freeing endpoint resources that urb_dequeue can't */
|
||||
void (*endpoint_disable)(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *ep);
|
||||
|
||||
/* root hub support */
|
||||
int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
|
||||
int (*hub_control) (struct usb_hcd *hcd,
|
||||
u16 typeReq, u16 wValue, u16 wIndex,
|
||||
char *buf, u16 wLength);
|
||||
int (*bus_suspend)(struct usb_hcd *);
|
||||
int (*bus_resume)(struct usb_hcd *);
|
||||
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
|
||||
void (*hub_irq_enable)(struct usb_hcd *);
|
||||
/* Needed only if port-change IRQs are level-triggered */
|
||||
};
|
||||
|
||||
extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags);
|
||||
extern int usb_hcd_unlink_urb (struct urb *urb, int status);
|
||||
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
|
||||
extern void usb_hcd_endpoint_disable (struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep);
|
||||
extern int usb_hcd_get_frame_number (struct usb_device *udev);
|
||||
|
||||
extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
|
||||
struct device *dev, char *bus_name);
|
||||
extern struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd);
|
||||
extern void usb_put_hcd (struct usb_hcd *hcd);
|
||||
extern int usb_add_hcd(struct usb_hcd *hcd,
|
||||
unsigned int irqnum, unsigned long irqflags);
|
||||
extern void usb_remove_hcd(struct usb_hcd *hcd);
|
||||
|
||||
struct platform_device;
|
||||
extern void usb_hcd_platform_shutdown(struct platform_device* dev);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
struct pci_dev;
|
||||
struct pci_device_id;
|
||||
extern int usb_hcd_pci_probe (struct pci_dev *dev,
|
||||
const struct pci_device_id *id);
|
||||
extern void usb_hcd_pci_remove (struct pci_dev *dev);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t state);
|
||||
extern int usb_hcd_pci_resume (struct pci_dev *dev);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
extern void usb_hcd_pci_shutdown (struct pci_dev *dev);
|
||||
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
/* pci-ish (pdev null is ok) buffer alloc/mapping support */
|
||||
int hcd_buffer_create (struct usb_hcd *hcd);
|
||||
void hcd_buffer_destroy (struct usb_hcd *hcd);
|
||||
|
||||
void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
|
||||
gfp_t mem_flags, dma_addr_t *dma);
|
||||
void hcd_buffer_free (struct usb_bus *bus, size_t size,
|
||||
void *addr, dma_addr_t dma);
|
||||
|
||||
/* generic bus glue, needed for host controllers that don't use PCI */
|
||||
extern irqreturn_t usb_hcd_irq (int irq, void *__hcd);
|
||||
|
||||
extern void usb_hc_died (struct usb_hcd *hcd);
|
||||
extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Enumeration is only for the hub driver, or HCD virtual root hubs */
|
||||
extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
||||
struct usb_bus *, unsigned port);
|
||||
extern int usb_new_device(struct usb_device *dev);
|
||||
extern void usb_disconnect(struct usb_device **);
|
||||
|
||||
extern int usb_get_configuration(struct usb_device *dev);
|
||||
extern void usb_destroy_configuration(struct usb_device *dev);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* HCD Root Hub support
|
||||
*/
|
||||
|
||||
#include "hub.h"
|
||||
|
||||
/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
|
||||
#define DeviceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define DeviceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
|
||||
#define InterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
#define EndpointRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define EndpointOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
/* class requests from the USB 2.0 hub spec, table 11-15 */
|
||||
/* GetBusState and SetHubDescriptor are optional, omitted */
|
||||
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
|
||||
#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
|
||||
#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
|
||||
#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
|
||||
#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
|
||||
#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
|
||||
#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Generic bandwidth allocation constants/support
|
||||
*/
|
||||
#define FRAME_TIME_USECS 1000L
|
||||
#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
|
||||
/* Trying not to use worst-case bit-stuffing
|
||||
of (7/6 * 8 * bytecount) = 9.33 * bytecount */
|
||||
/* bytecount = data payload byte count */
|
||||
|
||||
#define NS_TO_US(ns) ((ns + 500L) / 1000L)
|
||||
/* convert & round nanoseconds to microseconds */
|
||||
|
||||
|
||||
/*
|
||||
* Full/low speed bandwidth allocation constants/support.
|
||||
*/
|
||||
#define BW_HOST_DELAY 1000L /* nanoseconds */
|
||||
#define BW_HUB_LS_SETUP 333L /* nanoseconds */
|
||||
/* 4 full-speed bit times (est.) */
|
||||
|
||||
#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
|
||||
#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
|
||||
#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
|
||||
|
||||
/*
|
||||
* Ceiling [nano/micro]seconds (typical) for that many bytes at high speed
|
||||
* ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
|
||||
* to preallocate bandwidth)
|
||||
*/
|
||||
#define USB2_HOST_DELAY 5 /* nsec, guess */
|
||||
#define HS_NSECS(bytes) ( ((55 * 8 * 2083) \
|
||||
+ (2083UL * (3 + BitTime(bytes))))/1000 \
|
||||
+ USB2_HOST_DELAY)
|
||||
#define HS_NSECS_ISO(bytes) ( ((38 * 8 * 2083) \
|
||||
+ (2083UL * (3 + BitTime(bytes))))/1000 \
|
||||
+ USB2_HOST_DELAY)
|
||||
#define HS_USECS(bytes) NS_TO_US (HS_NSECS(bytes))
|
||||
#define HS_USECS_ISO(bytes) NS_TO_US (HS_NSECS_ISO(bytes))
|
||||
|
||||
extern long usb_calc_bus_time (int speed, int is_input,
|
||||
int isoc, int bytecount);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
extern void usb_set_device_state(struct usb_device *udev,
|
||||
enum usb_device_state new_state);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* exported only within usbcore */
|
||||
|
||||
extern struct list_head usb_bus_list;
|
||||
extern struct mutex usb_bus_list_lock;
|
||||
extern wait_queue_head_t usb_kill_urb_queue;
|
||||
|
||||
extern void usb_enable_root_hub_irq (struct usb_bus *bus);
|
||||
|
||||
extern int usb_find_interface_driver (struct usb_device *dev,
|
||||
struct usb_interface *interface);
|
||||
|
||||
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
|
||||
extern void usb_root_hub_lost_power (struct usb_device *rhdev);
|
||||
extern int hcd_bus_suspend (struct usb_bus *bus);
|
||||
extern int hcd_bus_resume (struct usb_bus *bus);
|
||||
#else
|
||||
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int hcd_bus_suspend(struct usb_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int hcd_bus_resume (struct usb_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*
|
||||
* USB device fs stuff
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_DEVICEFS
|
||||
|
||||
/*
|
||||
* these are expected to be called from the USB core/hub thread
|
||||
* with the kernel lock held
|
||||
*/
|
||||
extern void usbfs_update_special (void);
|
||||
extern int usbfs_init(void);
|
||||
extern void usbfs_cleanup(void);
|
||||
|
||||
#else /* CONFIG_USB_DEVICEFS */
|
||||
|
||||
static inline void usbfs_update_special (void) {}
|
||||
static inline int usbfs_init(void) { return 0; }
|
||||
static inline void usbfs_cleanup(void) { }
|
||||
|
||||
#endif /* CONFIG_USB_DEVICEFS */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_USB_MON)
|
||||
|
||||
struct usb_mon_operations {
|
||||
void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
|
||||
void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
|
||||
void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
|
||||
/* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
|
||||
};
|
||||
|
||||
extern struct usb_mon_operations *mon_ops;
|
||||
|
||||
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
|
||||
{
|
||||
if (bus->monitored)
|
||||
(*mon_ops->urb_submit)(bus, urb);
|
||||
}
|
||||
|
||||
static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
|
||||
int error)
|
||||
{
|
||||
if (bus->monitored)
|
||||
(*mon_ops->urb_submit_error)(bus, urb, error);
|
||||
}
|
||||
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
|
||||
{
|
||||
if (bus->monitored)
|
||||
(*mon_ops->urb_complete)(bus, urb);
|
||||
}
|
||||
|
||||
int usb_mon_register(struct usb_mon_operations *ops);
|
||||
void usb_mon_deregister(void);
|
||||
|
||||
#else
|
||||
|
||||
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
|
||||
static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
|
||||
int error) {}
|
||||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
|
||||
|
||||
#endif /* CONFIG_USB_MON */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
|
||||
// bleech -- resurfaced in 2.4.11 or 2.4.12
|
||||
#define bitmap DeviceRemovable
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* random stuff */
|
||||
|
||||
#define RUN_CONTEXT (in_irq () ? "in_irq" \
|
||||
: (in_interrupt () ? "in_interrupt" : "can sleep"))
|
||||
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
3119
drivers/usb/core/hub.c
Normal file
3119
drivers/usb/core/hub.c
Normal file
File diff suppressed because it is too large
Load Diff
195
drivers/usb/core/hub.h
Normal file
195
drivers/usb/core/hub.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifndef __LINUX_HUB_H
|
||||
#define __LINUX_HUB_H
|
||||
|
||||
/*
|
||||
* Hub protocol and driver data structures.
|
||||
*
|
||||
* Some of these are known to the "virtual root hub" code
|
||||
* in host controller drivers.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/compiler.h> /* likely()/unlikely() */
|
||||
|
||||
/*
|
||||
* Hub request types
|
||||
*/
|
||||
|
||||
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
|
||||
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
|
||||
|
||||
/*
|
||||
* Hub class requests
|
||||
* See USB 2.0 spec Table 11-16
|
||||
*/
|
||||
#define HUB_CLEAR_TT_BUFFER 8
|
||||
#define HUB_RESET_TT 9
|
||||
#define HUB_GET_TT_STATE 10
|
||||
#define HUB_STOP_TT 11
|
||||
|
||||
/*
|
||||
* Hub Class feature numbers
|
||||
* See USB 2.0 spec Table 11-17
|
||||
*/
|
||||
#define C_HUB_LOCAL_POWER 0
|
||||
#define C_HUB_OVER_CURRENT 1
|
||||
|
||||
/*
|
||||
* Port feature numbers
|
||||
* See USB 2.0 spec Table 11-17
|
||||
*/
|
||||
#define USB_PORT_FEAT_CONNECTION 0
|
||||
#define USB_PORT_FEAT_ENABLE 1
|
||||
#define USB_PORT_FEAT_SUSPEND 2
|
||||
#define USB_PORT_FEAT_OVER_CURRENT 3
|
||||
#define USB_PORT_FEAT_RESET 4
|
||||
#define USB_PORT_FEAT_POWER 8
|
||||
#define USB_PORT_FEAT_LOWSPEED 9
|
||||
#define USB_PORT_FEAT_HIGHSPEED 10
|
||||
#define USB_PORT_FEAT_C_CONNECTION 16
|
||||
#define USB_PORT_FEAT_C_ENABLE 17
|
||||
#define USB_PORT_FEAT_C_SUSPEND 18
|
||||
#define USB_PORT_FEAT_C_OVER_CURRENT 19
|
||||
#define USB_PORT_FEAT_C_RESET 20
|
||||
#define USB_PORT_FEAT_TEST 21
|
||||
#define USB_PORT_FEAT_INDICATOR 22
|
||||
|
||||
/*
|
||||
* Hub Status and Hub Change results
|
||||
* See USB 2.0 spec Table 11-19 and Table 11-20
|
||||
*/
|
||||
struct usb_port_status {
|
||||
__le16 wPortStatus;
|
||||
__le16 wPortChange;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* wPortStatus bit field
|
||||
* See USB 2.0 spec Table 11-21
|
||||
*/
|
||||
#define USB_PORT_STAT_CONNECTION 0x0001
|
||||
#define USB_PORT_STAT_ENABLE 0x0002
|
||||
#define USB_PORT_STAT_SUSPEND 0x0004
|
||||
#define USB_PORT_STAT_OVERCURRENT 0x0008
|
||||
#define USB_PORT_STAT_RESET 0x0010
|
||||
/* bits 5 to 7 are reserved */
|
||||
#define USB_PORT_STAT_POWER 0x0100
|
||||
#define USB_PORT_STAT_LOW_SPEED 0x0200
|
||||
#define USB_PORT_STAT_HIGH_SPEED 0x0400
|
||||
#define USB_PORT_STAT_TEST 0x0800
|
||||
#define USB_PORT_STAT_INDICATOR 0x1000
|
||||
/* bits 13 to 15 are reserved */
|
||||
|
||||
/*
|
||||
* wPortChange bit field
|
||||
* See USB 2.0 spec Table 11-22
|
||||
* Bits 0 to 4 shown, bits 5 to 15 are reserved
|
||||
*/
|
||||
#define USB_PORT_STAT_C_CONNECTION 0x0001
|
||||
#define USB_PORT_STAT_C_ENABLE 0x0002
|
||||
#define USB_PORT_STAT_C_SUSPEND 0x0004
|
||||
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
|
||||
#define USB_PORT_STAT_C_RESET 0x0010
|
||||
|
||||
/*
|
||||
* wHubCharacteristics (masks)
|
||||
* See USB 2.0 spec Table 11-13, offset 3
|
||||
*/
|
||||
#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */
|
||||
#define HUB_CHAR_COMPOUND 0x0004 /* D2 */
|
||||
#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */
|
||||
#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */
|
||||
#define HUB_CHAR_PORTIND 0x0080 /* D7 */
|
||||
|
||||
struct usb_hub_status {
|
||||
__le16 wHubStatus;
|
||||
__le16 wHubChange;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* Hub Status & Hub Change bit masks
|
||||
* See USB 2.0 spec Table 11-19 and Table 11-20
|
||||
* Bits 0 and 1 for wHubStatus and wHubChange
|
||||
* Bits 2 to 15 are reserved for both
|
||||
*/
|
||||
#define HUB_STATUS_LOCAL_POWER 0x0001
|
||||
#define HUB_STATUS_OVERCURRENT 0x0002
|
||||
#define HUB_CHANGE_LOCAL_POWER 0x0001
|
||||
#define HUB_CHANGE_OVERCURRENT 0x0002
|
||||
|
||||
|
||||
/*
|
||||
* Hub descriptor
|
||||
* See USB 2.0 spec Table 11-13
|
||||
*/
|
||||
|
||||
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
|
||||
#define USB_DT_HUB_NONVAR_SIZE 7
|
||||
|
||||
struct usb_hub_descriptor {
|
||||
__u8 bDescLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bNbrPorts;
|
||||
__le16 wHubCharacteristics;
|
||||
__u8 bPwrOn2PwrGood;
|
||||
__u8 bHubContrCurrent;
|
||||
/* add 1 bit for hub status change; round to bytes */
|
||||
__u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
|
||||
__u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* port indicator status selectors, tables 11-7 and 11-25 */
|
||||
#define HUB_LED_AUTO 0
|
||||
#define HUB_LED_AMBER 1
|
||||
#define HUB_LED_GREEN 2
|
||||
#define HUB_LED_OFF 3
|
||||
|
||||
enum hub_led_mode {
|
||||
INDICATOR_AUTO = 0,
|
||||
INDICATOR_CYCLE,
|
||||
/* software blinks for attention: software, hardware, reserved */
|
||||
INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
|
||||
INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
|
||||
INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct usb_device;
|
||||
|
||||
/* Transaction Translator Think Times, in bits */
|
||||
#define HUB_TTTT_8_BITS 0x00
|
||||
#define HUB_TTTT_16_BITS 0x20
|
||||
#define HUB_TTTT_24_BITS 0x40
|
||||
#define HUB_TTTT_32_BITS 0x60
|
||||
|
||||
/*
|
||||
* As of USB 2.0, full/low speed devices are segregated into trees.
|
||||
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
|
||||
* The other type grows from high speed hubs when they connect to
|
||||
* full/low speed devices using "Transaction Translators" (TTs).
|
||||
*
|
||||
* TTs should only be known to the hub driver, and high speed bus
|
||||
* drivers (only EHCI for now). They affect periodic scheduling and
|
||||
* sometimes control/bulk error recovery.
|
||||
*/
|
||||
struct usb_tt {
|
||||
struct usb_device *hub; /* upstream highspeed hub */
|
||||
int multi; /* true means one TT per port */
|
||||
unsigned think_time; /* think time in ns */
|
||||
|
||||
/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
|
||||
spinlock_t lock;
|
||||
struct list_head clear_list; /* of usb_tt_clear */
|
||||
struct work_struct kevent;
|
||||
};
|
||||
|
||||
struct usb_tt_clear {
|
||||
struct list_head clear_list;
|
||||
unsigned tt;
|
||||
u16 devinfo;
|
||||
};
|
||||
|
||||
extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
|
||||
|
||||
#endif /* __LINUX_HUB_H */
|
||||
759
drivers/usb/core/inode.c
Normal file
759
drivers/usb/core/inode.c
Normal file
@@ -0,0 +1,759 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* inode.c -- Inode/Dentry functions for the USB device file system.
|
||||
*
|
||||
* Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
|
||||
* Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.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; 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.
|
||||
*
|
||||
* History:
|
||||
* 0.1 04.01.2000 Created
|
||||
* 0.2 10.12.2001 converted to use the vfs layer better
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "usb.h"
|
||||
#include "hcd.h"
|
||||
|
||||
static struct super_operations usbfs_ops;
|
||||
static const struct file_operations default_file_operations;
|
||||
static struct vfsmount *usbfs_mount;
|
||||
static int usbfs_mount_count; /* = 0 */
|
||||
static int ignore_mount = 0;
|
||||
|
||||
static struct dentry *devices_usbfs_dentry;
|
||||
static int num_buses; /* = 0 */
|
||||
|
||||
static uid_t devuid; /* = 0 */
|
||||
static uid_t busuid; /* = 0 */
|
||||
static uid_t listuid; /* = 0 */
|
||||
static gid_t devgid; /* = 0 */
|
||||
static gid_t busgid; /* = 0 */
|
||||
static gid_t listgid; /* = 0 */
|
||||
static umode_t devmode = S_IWUSR | S_IRUGO;
|
||||
static umode_t busmode = S_IXUGO | S_IRUGO;
|
||||
static umode_t listmode = S_IRUGO;
|
||||
|
||||
enum {
|
||||
Opt_devuid, Opt_devgid, Opt_devmode,
|
||||
Opt_busuid, Opt_busgid, Opt_busmode,
|
||||
Opt_listuid, Opt_listgid, Opt_listmode,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
static match_table_t tokens = {
|
||||
{Opt_devuid, "devuid=%u"},
|
||||
{Opt_devgid, "devgid=%u"},
|
||||
{Opt_devmode, "devmode=%o"},
|
||||
{Opt_busuid, "busuid=%u"},
|
||||
{Opt_busgid, "busgid=%u"},
|
||||
{Opt_busmode, "busmode=%o"},
|
||||
{Opt_listuid, "listuid=%u"},
|
||||
{Opt_listgid, "listgid=%u"},
|
||||
{Opt_listmode, "listmode=%o"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
static int parse_options(struct super_block *s, char *data)
|
||||
{
|
||||
char *p;
|
||||
int option;
|
||||
|
||||
/* (re)set to defaults. */
|
||||
devuid = 0;
|
||||
busuid = 0;
|
||||
listuid = 0;
|
||||
devgid = 0;
|
||||
busgid = 0;
|
||||
listgid = 0;
|
||||
devmode = S_IWUSR | S_IRUGO;
|
||||
busmode = S_IXUGO | S_IRUGO;
|
||||
listmode = S_IRUGO;
|
||||
|
||||
while ((p = strsep(&data, ",")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int token;
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_devuid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
devuid = option;
|
||||
break;
|
||||
case Opt_devgid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
devgid = option;
|
||||
break;
|
||||
case Opt_devmode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
devmode = option & S_IRWXUGO;
|
||||
break;
|
||||
case Opt_busuid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
busuid = option;
|
||||
break;
|
||||
case Opt_busgid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
busgid = option;
|
||||
break;
|
||||
case Opt_busmode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
busmode = option & S_IRWXUGO;
|
||||
break;
|
||||
case Opt_listuid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
listuid = option;
|
||||
break;
|
||||
case Opt_listgid:
|
||||
if (match_int(&args[0], &option))
|
||||
return -EINVAL;
|
||||
listgid = option;
|
||||
break;
|
||||
case Opt_listmode:
|
||||
if (match_octal(&args[0], &option))
|
||||
return -EINVAL;
|
||||
listmode = option & S_IRWXUGO;
|
||||
break;
|
||||
default:
|
||||
err("usbfs: unrecognised mount option \"%s\" "
|
||||
"or missing value\n", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_special(struct dentry *special)
|
||||
{
|
||||
special->d_inode->i_uid = listuid;
|
||||
special->d_inode->i_gid = listgid;
|
||||
special->d_inode->i_mode = S_IFREG | listmode;
|
||||
}
|
||||
|
||||
static void update_dev(struct dentry *dev)
|
||||
{
|
||||
dev->d_inode->i_uid = devuid;
|
||||
dev->d_inode->i_gid = devgid;
|
||||
dev->d_inode->i_mode = S_IFREG | devmode;
|
||||
}
|
||||
|
||||
static void update_bus(struct dentry *bus)
|
||||
{
|
||||
struct dentry *dev = NULL;
|
||||
|
||||
bus->d_inode->i_uid = busuid;
|
||||
bus->d_inode->i_gid = busgid;
|
||||
bus->d_inode->i_mode = S_IFDIR | busmode;
|
||||
|
||||
mutex_lock(&bus->d_inode->i_mutex);
|
||||
|
||||
list_for_each_entry(dev, &bus->d_subdirs, d_u.d_child)
|
||||
if (dev->d_inode)
|
||||
update_dev(dev);
|
||||
|
||||
mutex_unlock(&bus->d_inode->i_mutex);
|
||||
}
|
||||
|
||||
static void update_sb(struct super_block *sb)
|
||||
{
|
||||
struct dentry *root = sb->s_root;
|
||||
struct dentry *bus = NULL;
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
mutex_lock_nested(&root->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
|
||||
list_for_each_entry(bus, &root->d_subdirs, d_u.d_child) {
|
||||
if (bus->d_inode) {
|
||||
switch (S_IFMT & bus->d_inode->i_mode) {
|
||||
case S_IFDIR:
|
||||
update_bus(bus);
|
||||
break;
|
||||
case S_IFREG:
|
||||
update_special(bus);
|
||||
break;
|
||||
default:
|
||||
warn("Unknown node %s mode %x found on remount!\n",bus->d_name.name,bus->d_inode->i_mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&root->d_inode->i_mutex);
|
||||
}
|
||||
|
||||
static int remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
/* If this is not a real mount,
|
||||
* i.e. it's a simple_pin_fs from create_special_files,
|
||||
* then ignore it.
|
||||
*/
|
||||
if (ignore_mount)
|
||||
return 0;
|
||||
|
||||
if (parse_options(sb, data)) {
|
||||
warn("usbfs: mount parameter error:");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (usbfs_mount && usbfs_mount->mnt_sb)
|
||||
update_sb(usbfs_mount->mnt_sb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev)
|
||||
{
|
||||
struct inode *inode = new_inode(sb);
|
||||
|
||||
if (inode) {
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = current->fsuid;
|
||||
inode->i_gid = current->fsgid;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
switch (mode & S_IFMT) {
|
||||
default:
|
||||
init_special_inode(inode, mode, dev);
|
||||
break;
|
||||
case S_IFREG:
|
||||
inode->i_fop = &default_file_operations;
|
||||
break;
|
||||
case S_IFDIR:
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
|
||||
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
||||
inc_nlink(inode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
|
||||
/* SMP-safe */
|
||||
static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
|
||||
dev_t dev)
|
||||
{
|
||||
struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
|
||||
int error = -EPERM;
|
||||
|
||||
if (dentry->d_inode)
|
||||
return -EEXIST;
|
||||
|
||||
if (inode) {
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
error = 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
int res;
|
||||
|
||||
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
|
||||
res = usbfs_mknod (dir, dentry, mode, 0);
|
||||
if (!res)
|
||||
inc_nlink(dir);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int usbfs_create (struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
mode = (mode & S_IALLUGO) | S_IFREG;
|
||||
return usbfs_mknod (dir, dentry, mode, 0);
|
||||
}
|
||||
|
||||
static inline int usbfs_positive (struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_inode && !d_unhashed(dentry);
|
||||
}
|
||||
|
||||
static int usbfs_empty (struct dentry *dentry)
|
||||
{
|
||||
struct list_head *list;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
|
||||
list_for_each(list, &dentry->d_subdirs) {
|
||||
struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
|
||||
if (usbfs_positive(de)) {
|
||||
spin_unlock(&dcache_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&dcache_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
mutex_lock(&inode->i_mutex);
|
||||
drop_nlink(dentry->d_inode);
|
||||
dput(dentry);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
d_delete(dentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int error = -ENOTEMPTY;
|
||||
struct inode * inode = dentry->d_inode;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
dentry_unhash(dentry);
|
||||
if (usbfs_empty(dentry)) {
|
||||
drop_nlink(dentry->d_inode);
|
||||
drop_nlink(dentry->d_inode);
|
||||
dput(dentry);
|
||||
inode->i_flags |= S_DEAD;
|
||||
drop_nlink(dir);
|
||||
error = 0;
|
||||
}
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (!error)
|
||||
d_delete(dentry);
|
||||
dput(dentry);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* default file operations */
|
||||
static ssize_t default_read_file (struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t default_write_file (struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
loff_t retval = -EINVAL;
|
||||
|
||||
mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
|
||||
switch(orig) {
|
||||
case 0:
|
||||
if (offset > 0) {
|
||||
file->f_pos = offset;
|
||||
retval = file->f_pos;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if ((offset + file->f_pos) > 0) {
|
||||
file->f_pos += offset;
|
||||
retval = file->f_pos;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int default_open (struct inode *inode, struct file *file)
|
||||
{
|
||||
if (inode->i_private)
|
||||
file->private_data = inode->i_private;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations default_file_operations = {
|
||||
.read = default_read_file,
|
||||
.write = default_write_file,
|
||||
.open = default_open,
|
||||
.llseek = default_file_lseek,
|
||||
};
|
||||
|
||||
static struct super_operations usbfs_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.remount_fs = remount,
|
||||
};
|
||||
|
||||
static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct dentry *root;
|
||||
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = USBDEVICE_SUPER_MAGIC;
|
||||
sb->s_op = &usbfs_ops;
|
||||
sb->s_time_gran = 1;
|
||||
inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0);
|
||||
|
||||
if (!inode) {
|
||||
dbg("%s: could not get inode!",__FUNCTION__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
root = d_alloc_root(inode);
|
||||
if (!root) {
|
||||
dbg("%s: could not get root dentry!",__FUNCTION__);
|
||||
iput(inode);
|
||||
return -ENOMEM;
|
||||
}
|
||||
sb->s_root = root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fs_create_by_name - create a file, given a name
|
||||
* @name: name of file
|
||||
* @mode: type of file
|
||||
* @parent: dentry of directory to create it in
|
||||
* @dentry: resulting dentry of file
|
||||
*
|
||||
* This function handles both regular files and directories.
|
||||
*/
|
||||
static int fs_create_by_name (const char *name, mode_t mode,
|
||||
struct dentry *parent, struct dentry **dentry)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
/* If the parent is not specified, we create it in the root.
|
||||
* We need the root dentry to do this, which is in the super
|
||||
* block. A pointer to that is in the struct vfsmount that we
|
||||
* have around.
|
||||
*/
|
||||
if (!parent ) {
|
||||
if (usbfs_mount && usbfs_mount->mnt_sb) {
|
||||
parent = usbfs_mount->mnt_sb->s_root;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parent) {
|
||||
dbg("Ah! can not find a parent!");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*dentry = NULL;
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
*dentry = lookup_one_len(name, parent, strlen(name));
|
||||
if (!IS_ERR(dentry)) {
|
||||
if ((mode & S_IFMT) == S_IFDIR)
|
||||
error = usbfs_mkdir (parent->d_inode, *dentry, mode);
|
||||
else
|
||||
error = usbfs_create (parent->d_inode, *dentry, mode);
|
||||
} else
|
||||
error = PTR_ERR(dentry);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct dentry *fs_create_file (const char *name, mode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops,
|
||||
uid_t uid, gid_t gid)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int error;
|
||||
|
||||
dbg("creating file '%s'",name);
|
||||
|
||||
error = fs_create_by_name (name, mode, parent, &dentry);
|
||||
if (error) {
|
||||
dentry = NULL;
|
||||
} else {
|
||||
if (dentry->d_inode) {
|
||||
if (data)
|
||||
dentry->d_inode->i_private = data;
|
||||
if (fops)
|
||||
dentry->d_inode->i_fop = fops;
|
||||
dentry->d_inode->i_uid = uid;
|
||||
dentry->d_inode->i_gid = gid;
|
||||
}
|
||||
}
|
||||
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static void fs_remove_file (struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
|
||||
if (!parent || !parent->d_inode)
|
||||
return;
|
||||
|
||||
mutex_lock_nested(&parent->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
if (usbfs_positive(dentry)) {
|
||||
if (dentry->d_inode) {
|
||||
if (S_ISDIR(dentry->d_inode->i_mode))
|
||||
usbfs_rmdir(parent->d_inode, dentry);
|
||||
else
|
||||
usbfs_unlink(parent->d_inode, dentry);
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int usb_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
|
||||
{
|
||||
return get_sb_single(fs_type, flags, data, usbfs_fill_super, mnt);
|
||||
}
|
||||
|
||||
static struct file_system_type usb_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "usbfs",
|
||||
.get_sb = usb_get_sb,
|
||||
.kill_sb = kill_litter_super,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int create_special_files (void)
|
||||
{
|
||||
struct dentry *parent;
|
||||
int retval;
|
||||
|
||||
/* the simple_pin_fs calls will call remount with no options
|
||||
* without this flag that would overwrite the real mount options (if any)
|
||||
*/
|
||||
ignore_mount = 1;
|
||||
|
||||
/* create the devices special file */
|
||||
retval = simple_pin_fs(&usb_fs_type, &usbfs_mount, &usbfs_mount_count);
|
||||
if (retval) {
|
||||
err ("Unable to get usbfs mount");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ignore_mount = 0;
|
||||
|
||||
parent = usbfs_mount->mnt_sb->s_root;
|
||||
devices_usbfs_dentry = fs_create_file ("devices",
|
||||
listmode | S_IFREG, parent,
|
||||
NULL, &usbfs_devices_fops,
|
||||
listuid, listgid);
|
||||
if (devices_usbfs_dentry == NULL) {
|
||||
err ("Unable to create devices usbfs file");
|
||||
retval = -ENODEV;
|
||||
goto error_clean_mounts;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
|
||||
error_clean_mounts:
|
||||
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void remove_special_files (void)
|
||||
{
|
||||
if (devices_usbfs_dentry)
|
||||
fs_remove_file (devices_usbfs_dentry);
|
||||
devices_usbfs_dentry = NULL;
|
||||
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
|
||||
}
|
||||
|
||||
void usbfs_update_special (void)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
if (devices_usbfs_dentry) {
|
||||
inode = devices_usbfs_dentry->d_inode;
|
||||
if (inode)
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbfs_add_bus(struct usb_bus *bus)
|
||||
{
|
||||
struct dentry *parent;
|
||||
char name[8];
|
||||
int retval;
|
||||
|
||||
/* create the special files if this is the first bus added */
|
||||
if (num_buses == 0) {
|
||||
retval = create_special_files();
|
||||
if (retval)
|
||||
return;
|
||||
}
|
||||
++num_buses;
|
||||
|
||||
sprintf (name, "%03d", bus->busnum);
|
||||
|
||||
parent = usbfs_mount->mnt_sb->s_root;
|
||||
bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
|
||||
bus, NULL, busuid, busgid);
|
||||
if (bus->usbfs_dentry == NULL) {
|
||||
err ("error creating usbfs bus entry");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbfs_remove_bus(struct usb_bus *bus)
|
||||
{
|
||||
if (bus->usbfs_dentry) {
|
||||
fs_remove_file (bus->usbfs_dentry);
|
||||
bus->usbfs_dentry = NULL;
|
||||
}
|
||||
|
||||
--num_buses;
|
||||
if (num_buses <= 0) {
|
||||
remove_special_files();
|
||||
num_buses = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbfs_add_device(struct usb_device *dev)
|
||||
{
|
||||
char name[8];
|
||||
int i;
|
||||
int i_size;
|
||||
|
||||
sprintf (name, "%03d", dev->devnum);
|
||||
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
|
||||
dev->bus->usbfs_dentry, dev,
|
||||
&usbfs_device_file_operations,
|
||||
devuid, devgid);
|
||||
if (dev->usbfs_dentry == NULL) {
|
||||
err ("error creating usbfs device entry");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the size of the device's file to be
|
||||
* equal to the size of the device descriptors. */
|
||||
i_size = sizeof (struct usb_device_descriptor);
|
||||
for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) {
|
||||
struct usb_config_descriptor *config =
|
||||
(struct usb_config_descriptor *)dev->rawdescriptors[i];
|
||||
i_size += le16_to_cpu(config->wTotalLength);
|
||||
}
|
||||
if (dev->usbfs_dentry->d_inode)
|
||||
dev->usbfs_dentry->d_inode->i_size = i_size;
|
||||
}
|
||||
|
||||
static void usbfs_remove_device(struct usb_device *dev)
|
||||
{
|
||||
struct dev_state *ds;
|
||||
struct siginfo sinfo;
|
||||
|
||||
if (dev->usbfs_dentry) {
|
||||
fs_remove_file (dev->usbfs_dentry);
|
||||
dev->usbfs_dentry = NULL;
|
||||
}
|
||||
while (!list_empty(&dev->filelist)) {
|
||||
ds = list_entry(dev->filelist.next, struct dev_state, list);
|
||||
wake_up_all(&ds->wait);
|
||||
list_del_init(&ds->list);
|
||||
if (ds->discsignr) {
|
||||
sinfo.si_signo = ds->discsignr;
|
||||
sinfo.si_errno = EPIPE;
|
||||
sinfo.si_code = SI_ASYNCIO;
|
||||
sinfo.si_addr = ds->disccontext;
|
||||
kill_pid_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)
|
||||
{
|
||||
switch (action) {
|
||||
case USB_DEVICE_ADD:
|
||||
usbfs_add_device(dev);
|
||||
break;
|
||||
case USB_DEVICE_REMOVE:
|
||||
usbfs_remove_device(dev);
|
||||
break;
|
||||
case USB_BUS_ADD:
|
||||
usbfs_add_bus(dev);
|
||||
break;
|
||||
case USB_BUS_REMOVE:
|
||||
usbfs_remove_bus(dev);
|
||||
}
|
||||
|
||||
usbfs_update_special();
|
||||
usbfs_conn_disc_event();
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block usbfs_nb = {
|
||||
.notifier_call = usbfs_notify,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static struct proc_dir_entry *usbdir = NULL;
|
||||
|
||||
int __init usbfs_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = register_filesystem(&usb_fs_type);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
usb_register_notify(&usbfs_nb);
|
||||
|
||||
/* create mount point for usbfs */
|
||||
usbdir = proc_mkdir("usb", proc_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usbfs_cleanup(void)
|
||||
{
|
||||
usb_unregister_notify(&usbfs_nb);
|
||||
unregister_filesystem(&usb_fs_type);
|
||||
if (usbdir)
|
||||
remove_proc_entry("usb", proc_bus);
|
||||
}
|
||||
|
||||
1588
drivers/usb/core/message.c
Normal file
1588
drivers/usb/core/message.c
Normal file
File diff suppressed because it is too large
Load Diff
68
drivers/usb/core/notify.c
Normal file
68
drivers/usb/core/notify.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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/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_notifier() 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);
|
||||
}
|
||||
112
drivers/usb/core/otg_whitelist.h
Normal file
112
drivers/usb/core/otg_whitelist.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 Whitelist is the OTG "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;
|
||||
|
||||
/* possible in developer configs only! */
|
||||
if (!dev->bus->otg_port)
|
||||
return 1;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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));
|
||||
#ifdef CONFIG_USB_OTG_WHITELIST
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
78
drivers/usb/core/quirks.c
Normal file
78
drivers/usb/core/quirks.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 "usb.h"
|
||||
|
||||
/* List of quirky USB devices. Please keep this list ordered by:
|
||||
* 1) Vendor ID
|
||||
* 2) Product ID
|
||||
* 3) Class ID
|
||||
*
|
||||
* as we want specific devices to be overridden first, and only after that, any
|
||||
* class specific quirks.
|
||||
*
|
||||
* Right now the logic aborts if it finds a valid device in the table, we might
|
||||
* want to change that in the future if it turns out that a whole class of
|
||||
* devices is broken...
|
||||
*/
|
||||
static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* HP 5300/5370C scanner */
|
||||
{ USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },
|
||||
/* Seiko Epson Corp - Perfection 1670 */
|
||||
{ USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
/* Elsa MicroLink 56k (V.250) */
|
||||
{ USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
||||
static void usb_autosuspend_quirk(struct usb_device *udev)
|
||||
{
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
/* disable autosuspend, but allow the user to re-enable it via sysfs */
|
||||
udev->autosuspend_delay = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct usb_device_id *find_id(struct usb_device *udev)
|
||||
{
|
||||
const struct usb_device_id *id = usb_quirk_list;
|
||||
|
||||
for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
|
||||
id->driver_info; id++) {
|
||||
if (usb_match_device(udev, id))
|
||||
return id;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect any quirks the device has, and do any housekeeping for it if needed.
|
||||
*/
|
||||
void usb_detect_quirks(struct usb_device *udev)
|
||||
{
|
||||
const struct usb_device_id *id = usb_quirk_list;
|
||||
|
||||
id = find_id(udev);
|
||||
if (id)
|
||||
udev->quirks = (u32)(id->driver_info);
|
||||
if (udev->quirks)
|
||||
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
|
||||
udev->quirks);
|
||||
|
||||
/* do any special quirk handling here if needed */
|
||||
if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND)
|
||||
usb_autosuspend_quirk(udev);
|
||||
}
|
||||
457
drivers/usb/core/sysfs.c
Normal file
457
drivers/usb/core/sysfs.c
Normal file
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* drivers/usb/core/sysfs.c
|
||||
*
|
||||
* (C) Copyright 2002 David Brownell
|
||||
* (C) Copyright 2002,2004 Greg Kroah-Hartman
|
||||
* (C) Copyright 2002,2004 IBM Corp.
|
||||
*
|
||||
* All of the sysfs file attributes for usb devices and interfaces.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include "usb.h"
|
||||
|
||||
/* Active configuration fields */
|
||||
#define usb_actconfig_show(field, multiplier, format_string) \
|
||||
static ssize_t show_##field(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_device *udev; \
|
||||
struct usb_host_config *actconfig; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
actconfig = udev->actconfig; \
|
||||
if (actconfig) \
|
||||
return sprintf(buf, format_string, \
|
||||
actconfig->desc.field * multiplier); \
|
||||
else \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
#define usb_actconfig_attr(field, multiplier, format_string) \
|
||||
usb_actconfig_show(field, multiplier, format_string) \
|
||||
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
|
||||
|
||||
usb_actconfig_attr(bNumInterfaces, 1, "%2d\n")
|
||||
usb_actconfig_attr(bmAttributes, 1, "%2x\n")
|
||||
usb_actconfig_attr(bMaxPower, 2, "%3dmA\n")
|
||||
|
||||
static ssize_t show_configuration_string(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct usb_host_config *actconfig;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
actconfig = udev->actconfig;
|
||||
if ((!actconfig) || (!actconfig->string))
|
||||
return 0;
|
||||
return sprintf(buf, "%s\n", actconfig->string);
|
||||
}
|
||||
static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
|
||||
|
||||
/* configuration value is always present, and r/w */
|
||||
usb_actconfig_show(bConfigurationValue, 1, "%u\n");
|
||||
|
||||
static ssize_t
|
||||
set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int config, value;
|
||||
|
||||
if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255)
|
||||
return -EINVAL;
|
||||
usb_lock_device(udev);
|
||||
value = usb_set_configuration(udev, config);
|
||||
usb_unlock_device(udev);
|
||||
return (value < 0) ? value : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR,
|
||||
show_bConfigurationValue, set_bConfigurationValue);
|
||||
|
||||
/* String fields */
|
||||
#define usb_string_attr(name) \
|
||||
static ssize_t show_##name(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_device *udev; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
return sprintf(buf, "%s\n", udev->name); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
|
||||
|
||||
usb_string_attr(product);
|
||||
usb_string_attr(manufacturer);
|
||||
usb_string_attr(serial);
|
||||
|
||||
static ssize_t
|
||||
show_speed(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
char *speed;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_LOW:
|
||||
speed = "1.5";
|
||||
break;
|
||||
case USB_SPEED_UNKNOWN:
|
||||
case USB_SPEED_FULL:
|
||||
speed = "12";
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
speed = "480";
|
||||
break;
|
||||
default:
|
||||
speed = "unknown";
|
||||
}
|
||||
return sprintf(buf, "%s\n", speed);
|
||||
}
|
||||
static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
return sprintf(buf, "%d\n", udev->devnum);
|
||||
}
|
||||
static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_version(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
u16 bcdUSB;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
|
||||
return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff);
|
||||
}
|
||||
static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_maxchild(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
return sprintf(buf, "%d\n", udev->maxchild);
|
||||
}
|
||||
static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
return sprintf(buf, "0x%x\n", udev->quirks);
|
||||
}
|
||||
static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
static ssize_t
|
||||
show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_autosuspend(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
unsigned value, old;
|
||||
|
||||
if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ)
|
||||
return -EINVAL;
|
||||
value *= HZ;
|
||||
|
||||
old = udev->autosuspend_delay;
|
||||
udev->autosuspend_delay = value;
|
||||
if (value > 0 && old == 0)
|
||||
usb_try_autosuspend_device(udev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
|
||||
show_autosuspend, set_autosuspend);
|
||||
|
||||
static char power_group[] = "power";
|
||||
|
||||
static int add_power_attributes(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (is_usb_device(dev))
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void remove_power_attributes(struct device *dev)
|
||||
{
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define add_power_attributes(dev) 0
|
||||
#define remove_power_attributes(dev) do {} while (0)
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
/* Descriptor fields */
|
||||
#define usb_descriptor_attr_le16(field, format_string) \
|
||||
static ssize_t \
|
||||
show_##field(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct usb_device *udev; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
return sprintf(buf, format_string, \
|
||||
le16_to_cpu(udev->descriptor.field)); \
|
||||
} \
|
||||
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
|
||||
|
||||
usb_descriptor_attr_le16(idVendor, "%04x\n")
|
||||
usb_descriptor_attr_le16(idProduct, "%04x\n")
|
||||
usb_descriptor_attr_le16(bcdDevice, "%04x\n")
|
||||
|
||||
#define usb_descriptor_attr(field, format_string) \
|
||||
static ssize_t \
|
||||
show_##field(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct usb_device *udev; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
return sprintf(buf, format_string, udev->descriptor.field); \
|
||||
} \
|
||||
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
|
||||
|
||||
usb_descriptor_attr(bDeviceClass, "%02x\n")
|
||||
usb_descriptor_attr(bDeviceSubClass, "%02x\n")
|
||||
usb_descriptor_attr(bDeviceProtocol, "%02x\n")
|
||||
usb_descriptor_attr(bNumConfigurations, "%d\n")
|
||||
usb_descriptor_attr(bMaxPacketSize0, "%d\n")
|
||||
|
||||
static struct attribute *dev_attrs[] = {
|
||||
/* current configuration's attributes */
|
||||
&dev_attr_configuration.attr,
|
||||
&dev_attr_bNumInterfaces.attr,
|
||||
&dev_attr_bConfigurationValue.attr,
|
||||
&dev_attr_bmAttributes.attr,
|
||||
&dev_attr_bMaxPower.attr,
|
||||
/* device attributes */
|
||||
&dev_attr_idVendor.attr,
|
||||
&dev_attr_idProduct.attr,
|
||||
&dev_attr_bcdDevice.attr,
|
||||
&dev_attr_bDeviceClass.attr,
|
||||
&dev_attr_bDeviceSubClass.attr,
|
||||
&dev_attr_bDeviceProtocol.attr,
|
||||
&dev_attr_bNumConfigurations.attr,
|
||||
&dev_attr_bMaxPacketSize0.attr,
|
||||
&dev_attr_speed.attr,
|
||||
&dev_attr_devnum.attr,
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_maxchild.attr,
|
||||
&dev_attr_quirks.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group dev_attr_grp = {
|
||||
.attrs = dev_attrs,
|
||||
};
|
||||
|
||||
int usb_create_sysfs_dev_files(struct usb_device *udev)
|
||||
{
|
||||
struct device *dev = &udev->dev;
|
||||
int retval;
|
||||
|
||||
retval = sysfs_create_group(&dev->kobj, &dev_attr_grp);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = add_power_attributes(dev);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
if (udev->manufacturer) {
|
||||
retval = device_create_file(dev, &dev_attr_manufacturer);
|
||||
if (retval)
|
||||
goto error;
|
||||
}
|
||||
if (udev->product) {
|
||||
retval = device_create_file(dev, &dev_attr_product);
|
||||
if (retval)
|
||||
goto error;
|
||||
}
|
||||
if (udev->serial) {
|
||||
retval = device_create_file(dev, &dev_attr_serial);
|
||||
if (retval)
|
||||
goto error;
|
||||
}
|
||||
retval = usb_create_ep_files(dev, &udev->ep0, udev);
|
||||
if (retval)
|
||||
goto error;
|
||||
return 0;
|
||||
error:
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usb_remove_sysfs_dev_files(struct usb_device *udev)
|
||||
{
|
||||
struct device *dev = &udev->dev;
|
||||
|
||||
usb_remove_ep_files(&udev->ep0);
|
||||
device_remove_file(dev, &dev_attr_manufacturer);
|
||||
device_remove_file(dev, &dev_attr_product);
|
||||
device_remove_file(dev, &dev_attr_serial);
|
||||
remove_power_attributes(dev);
|
||||
sysfs_remove_group(&dev->kobj, &dev_attr_grp);
|
||||
}
|
||||
|
||||
/* Interface fields */
|
||||
#define usb_intf_attr(field, format_string) \
|
||||
static ssize_t \
|
||||
show_##field(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct usb_interface *intf = to_usb_interface(dev); \
|
||||
\
|
||||
return sprintf(buf, format_string, \
|
||||
intf->cur_altsetting->desc.field); \
|
||||
} \
|
||||
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
|
||||
|
||||
usb_intf_attr(bInterfaceNumber, "%02x\n")
|
||||
usb_intf_attr(bAlternateSetting, "%2d\n")
|
||||
usb_intf_attr(bNumEndpoints, "%02x\n")
|
||||
usb_intf_attr(bInterfaceClass, "%02x\n")
|
||||
usb_intf_attr(bInterfaceSubClass, "%02x\n")
|
||||
usb_intf_attr(bInterfaceProtocol, "%02x\n")
|
||||
|
||||
static ssize_t show_interface_string(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
struct usb_device *udev;
|
||||
int len;
|
||||
|
||||
intf = to_usb_interface(dev);
|
||||
udev = interface_to_usbdev(intf);
|
||||
len = snprintf(buf, 256, "%s", intf->cur_altsetting->string);
|
||||
if (len < 0)
|
||||
return 0;
|
||||
buf[len] = '\n';
|
||||
buf[len+1] = 0;
|
||||
return len+1;
|
||||
}
|
||||
static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL);
|
||||
|
||||
static ssize_t show_modalias(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
struct usb_device *udev;
|
||||
struct usb_host_interface *alt;
|
||||
|
||||
intf = to_usb_interface(dev);
|
||||
udev = interface_to_usbdev(intf);
|
||||
alt = intf->cur_altsetting;
|
||||
|
||||
return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
|
||||
"ic%02Xisc%02Xip%02X\n",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct),
|
||||
le16_to_cpu(udev->descriptor.bcdDevice),
|
||||
udev->descriptor.bDeviceClass,
|
||||
udev->descriptor.bDeviceSubClass,
|
||||
udev->descriptor.bDeviceProtocol,
|
||||
alt->desc.bInterfaceClass,
|
||||
alt->desc.bInterfaceSubClass,
|
||||
alt->desc.bInterfaceProtocol);
|
||||
}
|
||||
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
||||
|
||||
static struct attribute *intf_attrs[] = {
|
||||
&dev_attr_bInterfaceNumber.attr,
|
||||
&dev_attr_bAlternateSetting.attr,
|
||||
&dev_attr_bNumEndpoints.attr,
|
||||
&dev_attr_bInterfaceClass.attr,
|
||||
&dev_attr_bInterfaceSubClass.attr,
|
||||
&dev_attr_bInterfaceProtocol.attr,
|
||||
&dev_attr_modalias.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group intf_attr_grp = {
|
||||
.attrs = intf_attrs,
|
||||
};
|
||||
|
||||
static inline void usb_create_intf_ep_files(struct usb_interface *intf,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_interface *iface_desc;
|
||||
int i;
|
||||
|
||||
iface_desc = intf->cur_altsetting;
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
|
||||
usb_create_ep_files(&intf->dev, &iface_desc->endpoint[i],
|
||||
udev);
|
||||
}
|
||||
|
||||
static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_host_interface *iface_desc;
|
||||
int i;
|
||||
|
||||
iface_desc = intf->cur_altsetting;
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
|
||||
usb_remove_ep_files(&iface_desc->endpoint[i]);
|
||||
}
|
||||
|
||||
int usb_create_sysfs_intf_files(struct usb_interface *intf)
|
||||
{
|
||||
struct device *dev = &intf->dev;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
int retval;
|
||||
|
||||
retval = sysfs_create_group(&dev->kobj, &intf_attr_grp);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (alt->string == NULL)
|
||||
alt->string = usb_cache_string(udev, alt->desc.iInterface);
|
||||
if (alt->string)
|
||||
retval = device_create_file(dev, &dev_attr_interface);
|
||||
usb_create_intf_ep_files(intf, udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_remove_sysfs_intf_files(struct usb_interface *intf)
|
||||
{
|
||||
struct device *dev = &intf->dev;
|
||||
|
||||
usb_remove_intf_ep_files(intf);
|
||||
device_remove_file(dev, &dev_attr_interface);
|
||||
sysfs_remove_group(&dev->kobj, &intf_attr_grp);
|
||||
}
|
||||
488
drivers/usb/core/urb.c
Normal file
488
drivers/usb/core/urb.c
Normal file
@@ -0,0 +1,488 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
#include "hcd.h"
|
||||
|
||||
#define to_urb(d) container_of(d, struct urb, kref)
|
||||
|
||||
static void urb_destroy(struct kref *kref)
|
||||
{
|
||||
struct urb *urb = to_urb(kref);
|
||||
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);
|
||||
spin_lock_init(&urb->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, incrementes the usage counter, and returns a pointer to it.
|
||||
*
|
||||
* If no memory is available, NULL is returned.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
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) {
|
||||
err("alloc_urb: kmalloc failed");
|
||||
return NULL;
|
||||
}
|
||||
usb_init_urb(urb);
|
||||
return 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, that must be
|
||||
* done elsewhere.
|
||||
*/
|
||||
void usb_free_urb(struct urb *urb)
|
||||
{
|
||||
if (urb)
|
||||
kref_put(&urb->kref, urb_destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* A pointer to the urb with the incremented reference counter is returned.
|
||||
*/
|
||||
struct urb * usb_get_urb(struct urb *urb)
|
||||
{
|
||||
if (urb)
|
||||
kref_get(&urb->kref);
|
||||
return 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.
|
||||
*
|
||||
* Successful submissions return 0; otherwise this routine returns a
|
||||
* negative error number. 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 callacks). 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)
|
||||
{
|
||||
int pipe, temp, max;
|
||||
struct usb_device *dev;
|
||||
int is_out;
|
||||
|
||||
if (!urb || urb->hcpriv || !urb->complete)
|
||||
return -EINVAL;
|
||||
if (!(dev = urb->dev) ||
|
||||
(dev->state < USB_STATE_DEFAULT) ||
|
||||
(!dev->bus) || (dev->devnum <= 0))
|
||||
return -ENODEV;
|
||||
if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
|
||||
|| dev->state == USB_STATE_SUSPENDED)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
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
|
||||
*/
|
||||
pipe = urb->pipe;
|
||||
temp = usb_pipetype(pipe);
|
||||
is_out = usb_pipeout(pipe);
|
||||
|
||||
if (!usb_pipecontrol(pipe) && dev->state < USB_STATE_CONFIGURED)
|
||||
return -ENODEV;
|
||||
|
||||
/* FIXME there should be a sharable lock protecting us against
|
||||
* config/altsetting changes and disconnects, kicking in here.
|
||||
* (here == before maxpacket, and eventually endpoint type,
|
||||
* checks get made.)
|
||||
*/
|
||||
|
||||
max = usb_maxpacket(dev, pipe, is_out);
|
||||
if (max <= 0) {
|
||||
dev_dbg(&dev->dev,
|
||||
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
|
||||
usb_pipeendpoint(pipe), is_out ? "out" : "in",
|
||||
__FUNCTION__, 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 (temp == PIPE_ISOCHRONOUS) {
|
||||
int n, len;
|
||||
|
||||
/* "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;
|
||||
}
|
||||
}
|
||||
|
||||
/* the I/O buffer must be mapped/unmapped, except when length=0 */
|
||||
if (urb->transfer_buffer_length < 0)
|
||||
return -EMSGSIZE;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* stuff that drivers shouldn't do, but which shouldn't
|
||||
* cause problems in HCDs if they get it wrong.
|
||||
*/
|
||||
{
|
||||
unsigned int orig_flags = urb->transfer_flags;
|
||||
unsigned int allowed;
|
||||
|
||||
/* enforce simple/standard policy */
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
|
||||
URB_NO_INTERRUPT);
|
||||
switch (temp) {
|
||||
case PIPE_BULK:
|
||||
if (is_out)
|
||||
allowed |= URB_ZERO_PACKET;
|
||||
/* FALLTHROUGH */
|
||||
case PIPE_CONTROL:
|
||||
allowed |= URB_NO_FSBR; /* only affects UHCI */
|
||||
/* FALLTHROUGH */
|
||||
default: /* all non-iso endpoints */
|
||||
if (!is_out)
|
||||
allowed |= URB_SHORT_NOT_OK;
|
||||
break;
|
||||
case PIPE_ISOCHRONOUS:
|
||||
allowed |= URB_ISO_ASAP;
|
||||
break;
|
||||
}
|
||||
urb->transfer_flags &= allowed;
|
||||
|
||||
/* fail if submitter gave bogus flags */
|
||||
if (urb->transfer_flags != orig_flags) {
|
||||
err("BOGUS urb flags, %x --> %x",
|
||||
orig_flags, urb->transfer_flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* 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 (temp) {
|
||||
case PIPE_ISOCHRONOUS:
|
||||
case PIPE_INTERRUPT:
|
||||
/* too small? */
|
||||
if (urb->interval <= 0)
|
||||
return -EINVAL;
|
||||
/* too big? */
|
||||
switch (dev->speed) {
|
||||
case USB_SPEED_HIGH: /* units are microframes */
|
||||
// NOTE usb handles 2^15
|
||||
if (urb->interval > (1024 * 8))
|
||||
urb->interval = 1024 * 8;
|
||||
temp = 1024 * 8;
|
||||
break;
|
||||
case USB_SPEED_FULL: /* units are frames/msec */
|
||||
case USB_SPEED_LOW:
|
||||
if (temp == PIPE_INTERRUPT) {
|
||||
if (urb->interval > 255)
|
||||
return -EINVAL;
|
||||
// NOTE ohci only handles up to 32
|
||||
temp = 128;
|
||||
} else {
|
||||
if (urb->interval > 1024)
|
||||
urb->interval = 1024;
|
||||
// NOTE usb and ohci handle up to 2^15
|
||||
temp = 1024;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
/* power of two? */
|
||||
while (temp > urb->interval)
|
||||
temp >>= 1;
|
||||
urb->interval = temp;
|
||||
}
|
||||
|
||||
return usb_hcd_submit_urb(urb, mem_flags);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 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 the requests's completion handler will
|
||||
* be called with a status code indicating that the request has been
|
||||
* canceled (rather than any other code) and will quickly be removed
|
||||
* from host controller data structures.
|
||||
*
|
||||
* This request is always asynchronous.
|
||||
* Success is indicated by returning -EINPROGRESS,
|
||||
* at which time the URB will normally have been unlinked but not yet
|
||||
* given back to the device driver. When it is called, the completion
|
||||
* function will see urb->status == -ECONNRESET. Failure is indicated
|
||||
* by any other return value. Unlinking will fail when the 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.
|
||||
*
|
||||
* Unlinking and Endpoint Queues:
|
||||
*
|
||||
* 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 stops, at least until that URB's completion routine
|
||||
* returns. It is guaranteed that the 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. Normally the same behavior and guarantees
|
||||
* apply when an URB terminates because it was unlinked; however if an
|
||||
* URB is unlinked before the hardware has started to execute it, then
|
||||
* its queue is not guaranteed to stop until all the preceding URBs have
|
||||
* completed.
|
||||
*
|
||||
* This means that USB device drivers can safely build deep queues for
|
||||
* large or complex transfers, and clean them up reliably after any sort
|
||||
* of aborted transfer by unlinking all pending URBs at the first fault.
|
||||
*
|
||||
* Note that an URB terminating early because a short packet was received
|
||||
* will count as an error if and only if the URB_SHORT_NOT_OK flag is set.
|
||||
* Also, that all unlinks performed in any URB completion handler must
|
||||
* be asynchronous.
|
||||
*
|
||||
* Queues for isochronous endpoints are treated differently, because they
|
||||
* advance at fixed rates. Such queues do not stop when an URB is unlinked.
|
||||
* An unlinked URB may leave a gap in the stream of packets. It is undefined
|
||||
* whether such gaps can be filled in.
|
||||
*
|
||||
* When a control URB terminates with an error, it is likely that the
|
||||
* status stage of the transfer will not take place, even if it is merely
|
||||
* a soft error resulting from a short-packet with URB_SHORT_NOT_OK set.
|
||||
*/
|
||||
int usb_unlink_urb(struct urb *urb)
|
||||
{
|
||||
if (!urb)
|
||||
return -EINVAL;
|
||||
if (!(urb->dev && urb->dev->bus))
|
||||
return -ENODEV;
|
||||
return usb_hcd_unlink_urb(urb, -ECONNRESET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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().
|
||||
*/
|
||||
void usb_kill_urb(struct urb *urb)
|
||||
{
|
||||
might_sleep();
|
||||
if (!(urb && urb->dev && urb->dev->bus))
|
||||
return;
|
||||
spin_lock_irq(&urb->lock);
|
||||
++urb->reject;
|
||||
spin_unlock_irq(&urb->lock);
|
||||
|
||||
usb_hcd_unlink_urb(urb, -ENOENT);
|
||||
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
|
||||
|
||||
spin_lock_irq(&urb->lock);
|
||||
--urb->reject;
|
||||
spin_unlock_irq(&urb->lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(usb_init_urb);
|
||||
EXPORT_SYMBOL(usb_alloc_urb);
|
||||
EXPORT_SYMBOL(usb_free_urb);
|
||||
EXPORT_SYMBOL(usb_get_urb);
|
||||
EXPORT_SYMBOL(usb_submit_urb);
|
||||
EXPORT_SYMBOL(usb_unlink_urb);
|
||||
EXPORT_SYMBOL(usb_kill_urb);
|
||||
|
||||
1001
drivers/usb/core/usb.c
Normal file
1001
drivers/usb/core/usb.c
Normal file
File diff suppressed because it is too large
Load Diff
156
drivers/usb/core/usb.h
Normal file
156
drivers/usb/core/usb.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/* 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 int 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_files(struct device *parent, struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev);
|
||||
extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
|
||||
|
||||
extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
|
||||
extern void usb_disable_interface (struct usb_device *dev,
|
||||
struct usb_interface *intf);
|
||||
extern void usb_release_interface_cache(struct kref *ref);
|
||||
extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
|
||||
extern void usb_detect_quirks(struct usb_device *udev);
|
||||
|
||||
extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
unsigned int size);
|
||||
extern char *usb_cache_string(struct usb_device *udev, int index);
|
||||
extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
||||
|
||||
extern void usb_kick_khubd(struct usb_device *dev);
|
||||
extern void usb_resume_root_hub(struct usb_device *dev);
|
||||
extern int usb_match_device(struct usb_device *dev,
|
||||
const struct usb_device_id *id);
|
||||
|
||||
extern int usb_hub_init(void);
|
||||
extern void usb_hub_cleanup(void);
|
||||
extern int usb_major_init(void);
|
||||
extern void usb_major_cleanup(void);
|
||||
extern int usb_host_init(void);
|
||||
extern void usb_host_cleanup(void);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
|
||||
extern int usb_resume_both(struct usb_device *udev);
|
||||
extern int usb_port_suspend(struct usb_device *dev);
|
||||
extern int usb_port_resume(struct usb_device *dev);
|
||||
|
||||
static inline void usb_pm_lock(struct usb_device *udev)
|
||||
{
|
||||
mutex_lock_nested(&udev->pm_mutex, udev->level);
|
||||
}
|
||||
|
||||
static inline void usb_pm_unlock(struct usb_device *udev)
|
||||
{
|
||||
mutex_unlock(&udev->pm_mutex);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define usb_suspend_both(udev, msg) 0
|
||||
static inline int usb_resume_both(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#define usb_port_suspend(dev) 0
|
||||
#define usb_port_resume(dev) 0
|
||||
static inline void usb_pm_lock(struct usb_device *udev) {}
|
||||
static inline void usb_pm_unlock(struct usb_device *udev) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
extern void usb_autosuspend_device(struct usb_device *udev);
|
||||
extern void usb_try_autosuspend_device(struct usb_device *udev);
|
||||
extern int usb_autoresume_device(struct usb_device *udev);
|
||||
|
||||
#else
|
||||
|
||||
#define usb_autosuspend_device(udev) do {} while (0)
|
||||
#define usb_try_autosuspend_device(udev) do {} while (0)
|
||||
static inline int usb_autoresume_device(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern struct workqueue_struct *ksuspend_usb_wq;
|
||||
extern struct bus_type usb_bus_type;
|
||||
extern struct usb_device_driver usb_generic_driver;
|
||||
|
||||
/* Here's how we tell apart devices and interfaces. Luckily there's
|
||||
* no such thing as a platform USB device, so we can steal the use
|
||||
* of the platform_data field. */
|
||||
|
||||
static inline int is_usb_device(const struct device *dev)
|
||||
{
|
||||
return dev->platform_data == &usb_generic_driver;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Interfaces and their "power state" are owned by usbcore */
|
||||
|
||||
static inline void mark_active(struct usb_interface *f)
|
||||
{
|
||||
f->is_active = 1;
|
||||
}
|
||||
|
||||
static inline void mark_quiesced(struct usb_interface *f)
|
||||
{
|
||||
f->is_active = 0;
|
||||
}
|
||||
|
||||
static inline int is_active(const struct usb_interface *f)
|
||||
{
|
||||
return f->is_active;
|
||||
}
|
||||
|
||||
|
||||
/* for labeling diagnostics */
|
||||
extern const char *usbcore_name;
|
||||
|
||||
/* 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 usbfs_device_file_operations;
|
||||
extern void usbfs_conn_disc_event(void);
|
||||
|
||||
extern int usbdev_init(void);
|
||||
extern void usbdev_cleanup(void);
|
||||
|
||||
struct dev_state {
|
||||
struct list_head list; /* state list */
|
||||
struct usb_device *dev;
|
||||
struct file *file;
|
||||
spinlock_t lock; /* protects the async urb lists */
|
||||
struct list_head async_pending;
|
||||
struct list_head async_completed;
|
||||
wait_queue_head_t wait; /* wake up if a request completed */
|
||||
unsigned int discsignr;
|
||||
struct pid *disc_pid;
|
||||
uid_t disc_uid, disc_euid;
|
||||
void __user *disccontext;
|
||||
unsigned long ifclaimed;
|
||||
u32 secid;
|
||||
};
|
||||
|
||||
/* 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);
|
||||
|
||||
Reference in New Issue
Block a user