Commit b2fb8f66 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/linux/linux/BK/bleeding-2.5

into kroah.com:/home/linux/linux/BK/gregkh-2.5
parents 6865a163 7cbe5bbe
...@@ -299,6 +299,8 @@ KAO --> ...@@ -299,6 +299,8 @@ KAO -->
EHCI, OHCI, or UHCI. EHCI, OHCI, or UHCI.
</para> </para>
!Edrivers/usb/core/hcd.c !Edrivers/usb/core/hcd.c
!Edrivers/usb/core/hcd-pci.c
!Edrivers/usb/core/buffer.c
</sect1> </sect1>
</chapter> </chapter>
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
# Makefile for USB Core files and filesystem # Makefile for USB Core files and filesystem
# #
export-objs := usb.o hcd.o hcd-pci.o urb.o message.o file.o export-objs := usb.o hcd.o hcd-pci.o urb.o message.o file.o buffer.o
usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \ usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \
config.o file.o config.o file.o buffer.o
ifeq ($(CONFIG_PCI),y) ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o usbcore-objs += hcd-pci.o
......
/*
* DMA memory management for framework level HCD code (hc_driver)
*
* This implementation plugs in through generic "usb_bus" level methods,
* and works with real PCI, or when "pci device == null" makes sense.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h>
#include "hcd.h"
/*
* DMA-Consistent Buffers
*/
/* FIXME tune these based on pool statistics ... */
static const size_t pool_max [HCD_BUFFER_POOLS] = {
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
*
* Call this as part of initializing a host controller that uses the pci dma
* memory allocators. It initializes some pools of dma-consistent 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;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (!(size = pool_max [i]))
continue;
snprintf (name, sizeof name, "buffer-%d", size);
hcd->pool [i] = pci_pool_create (name, hcd->pdev,
size, size, 0, SLAB_KERNEL);
if (!hcd->pool [i]) {
hcd_buffer_destroy (hcd);
return -ENOMEM;
}
}
return 0;
}
EXPORT_SYMBOL (hcd_buffer_create);
/**
* hcd_buffer_destroy - deallocate buffer pools
* @hcd: the bus whose buffer pools are to be destroyed
*
* 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 pci_pool *pool = hcd->pool [i];
if (pool) {
pci_pool_destroy (pool);
hcd->pool [i] = 0;
}
}
}
EXPORT_SYMBOL (hcd_buffer_destroy);
/* sometimes alloc/free could use kmalloc with SLAB_DMA, for
* better sharing and to leverage mm/slab.c intelligence.
*/
void *hcd_buffer_alloc (
struct usb_bus *bus,
size_t size,
int mem_flags,
dma_addr_t *dma
)
{
struct usb_hcd *hcd = bus->hcpriv;
int i;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i])
return pci_pool_alloc (hcd->pool [i], mem_flags, dma);
}
return pci_alloc_consistent (hcd->pdev, size, dma);
}
void hcd_buffer_free (
struct usb_bus *bus,
size_t size,
void *addr,
dma_addr_t dma
)
{
struct usb_hcd *hcd = bus->hcpriv;
int i;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i]) {
pci_pool_free (hcd->pool [i], addr, dma);
return;
}
}
pci_free_consistent (hcd->pdev, size, addr, dma);
}
/*
* DMA-Mappings for arbitrary memory buffers
*/
int hcd_buffer_map (
struct usb_bus *bus,
void *addr,
dma_addr_t *dma,
size_t size,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
// FIXME pci_map_single() has no standard failure mode!
*dma = pci_map_single (hcd->pdev, addr, size,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
return 0;
}
void hcd_buffer_dmasync (
struct usb_bus *bus,
dma_addr_t dma,
size_t size,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_dma_sync_single (hcd->pdev, dma, size,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
void hcd_buffer_unmap (
struct usb_bus *bus,
dma_addr_t dma,
size_t size,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_unmap_single (hcd->pdev, dma, size,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
// FIXME DMA-Mappings for struct scatterlist
...@@ -130,10 +130,19 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ...@@ -130,10 +130,19 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
return retval; return retval;
} }
} }
pci_set_drvdata(dev, hcd); pci_set_drvdata (dev, hcd);
hcd->driver = driver; hcd->driver = driver;
hcd->description = driver->description; hcd->description = driver->description;
hcd->pdev = dev; hcd->pdev = dev;
hcd->self.bus_name = dev->slot_name;
hcd->product_desc = dev->name;
if ((retval = hcd_buffer_create (hcd)) != 0) {
clean_3:
driver->hcd_free (hcd);
goto clean_2;
}
info ("%s @ %s, %s", hcd->description, dev->slot_name, dev->name); info ("%s @ %s, %s", hcd->description, dev->slot_name, dev->name);
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
...@@ -154,8 +163,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ...@@ -154,8 +163,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
!= 0) { != 0) {
err ("request interrupt %s failed", bufp); err ("request interrupt %s failed", bufp);
retval = -EBUSY; retval = -EBUSY;
driver->hcd_free (hcd); goto clean_3;
goto clean_2;
} }
hcd->irq = dev->irq; hcd->irq = dev->irq;
...@@ -168,8 +176,6 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ...@@ -168,8 +176,6 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
usb_bus_init (&hcd->self); usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations; hcd->self.op = &usb_hcd_operations;
hcd->self.hcpriv = (void *) hcd; hcd->self.hcpriv = (void *) hcd;
hcd->self.bus_name = dev->slot_name;
hcd->product_desc = dev->name;
INIT_LIST_HEAD (&hcd->dev_list); INIT_LIST_HEAD (&hcd->dev_list);
...@@ -216,6 +222,7 @@ void usb_hcd_pci_remove (struct pci_dev *dev) ...@@ -216,6 +222,7 @@ void usb_hcd_pci_remove (struct pci_dev *dev)
usb_disconnect (&hub); usb_disconnect (&hub);
hcd->driver->stop (hcd); hcd->driver->stop (hcd);
hcd_buffer_destroy (hcd);
hcd->state = USB_STATE_HALT; hcd->state = USB_STATE_HALT;
free_irq (hcd->irq, hcd); free_irq (hcd->irq, hcd);
......
...@@ -454,7 +454,6 @@ static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) ...@@ -454,7 +454,6 @@ static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb)
/* rh_timer protected by hcd_data_lock */ /* rh_timer protected by hcd_data_lock */
if (timer_pending (&hcd->rh_timer) if (timer_pending (&hcd->rh_timer)
|| urb->status != -EINPROGRESS || urb->status != -EINPROGRESS
|| !HCD_IS_RUNNING (hcd->state)
|| urb->transfer_buffer_length < len) { || urb->transfer_buffer_length < len) {
dbg ("not queuing status urb, stat %d", urb->status); dbg ("not queuing status urb, stat %d", urb->status);
return -EINVAL; return -EINVAL;
...@@ -508,8 +507,12 @@ static void rh_report_status (unsigned long ptr) ...@@ -508,8 +507,12 @@ static void rh_report_status (unsigned long ptr)
BUG (); BUG ();
} }
spin_unlock_irqrestore (&hcd_data_lock, flags); spin_unlock_irqrestore (&hcd_data_lock, flags);
} else } else {
spin_unlock_irqrestore (&urb->lock, flags); spin_unlock_irqrestore (&urb->lock, flags);
spin_lock_irqsave (&hcd_data_lock, flags);
rh_status_urb (hcd, urb);
spin_unlock_irqrestore (&hcd_data_lock, flags);
}
} else { } else {
/* this urb's been unlinked */ /* this urb's been unlinked */
urb->hcpriv = 0; urb->hcpriv = 0;
...@@ -1245,6 +1248,11 @@ struct usb_operations usb_hcd_operations = { ...@@ -1245,6 +1248,11 @@ struct usb_operations usb_hcd_operations = {
.submit_urb = hcd_submit_urb, .submit_urb = hcd_submit_urb,
.unlink_urb = hcd_unlink_urb, .unlink_urb = hcd_unlink_urb,
.deallocate = hcd_free_dev, .deallocate = hcd_free_dev,
.buffer_alloc = hcd_buffer_alloc,
.buffer_free = hcd_buffer_free,
.buffer_map = hcd_buffer_map,
.buffer_dmasync = hcd_buffer_dmasync,
.buffer_unmap = hcd_buffer_unmap,
}; };
EXPORT_SYMBOL (usb_hcd_operations); EXPORT_SYMBOL (usb_hcd_operations);
......
...@@ -58,6 +58,9 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ ...@@ -58,6 +58,9 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
atomic_t resume_count; /* multiple resumes issue */ atomic_t resume_count; /* multiple resumes issue */
#endif #endif
#define HCD_BUFFER_POOLS 4
struct pci_pool *pool [HCD_BUFFER_POOLS];
int state; int state;
# define __ACTIVE 0x01 # define __ACTIVE 0x01
# define __SLEEPY 0x02 # define __SLEEPY 0x02
...@@ -109,6 +112,25 @@ struct usb_operations { ...@@ -109,6 +112,25 @@ struct usb_operations {
int (*get_frame_number) (struct usb_device *usb_dev); int (*get_frame_number) (struct usb_device *usb_dev);
int (*submit_urb) (struct urb *urb, int mem_flags); int (*submit_urb) (struct urb *urb, int mem_flags);
int (*unlink_urb) (struct urb *urb); int (*unlink_urb) (struct urb *urb);
/* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
int mem_flags,
dma_addr_t *dma);
void (*buffer_free)(struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
int (*buffer_map) (struct usb_bus *bus,
void *addr, dma_addr_t *dma,
size_t size, int direction);
void (*buffer_dmasync) (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
void (*buffer_unmap) (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
// FIXME also: buffer_sg_map (), buffer_sg_unmap ()
}; };
/* each driver provides one of these, and hardware init support */ /* each driver provides one of these, and hardware init support */
...@@ -181,6 +203,25 @@ extern int usb_hcd_pci_resume (struct pci_dev *dev); ...@@ -181,6 +203,25 @@ extern int usb_hcd_pci_resume (struct pci_dev *dev);
#endif /* CONFIG_PCI */ #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,
int mem_flags, dma_addr_t *dma);
void hcd_buffer_free (struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
int hcd_buffer_map (struct usb_bus *bus,
void *addr, dma_addr_t *dma,
size_t size, int direction);
void hcd_buffer_dmasync (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
void hcd_buffer_unmap (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
/* generic bus glue, needed for host controllers that don't use PCI */ /* generic bus glue, needed for host controllers that don't use PCI */
extern struct usb_operations usb_hcd_operations; extern struct usb_operations usb_hcd_operations;
extern void usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r); extern void usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
......
...@@ -863,9 +863,11 @@ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t ...@@ -863,9 +863,11 @@ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t
return 0; return 0;
udev = to_usb_device (dev); udev = to_usb_device (dev);
len = usb_string(udev, udev->descriptor.iProduct, buf, PAGE_SIZE); len = usb_string(udev, udev->descriptor.iProduct, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n'; buf[len] = '\n';
buf[len+1] = 0x00; buf[len+1] = 0;
return len+1; return len+1;
} }
static DEVICE_ATTR(product,"product",S_IRUGO,show_product,NULL); static DEVICE_ATTR(product,"product",S_IRUGO,show_product,NULL);
...@@ -881,9 +883,11 @@ show_manufacturer (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -881,9 +883,11 @@ show_manufacturer (struct device *dev, char *buf, size_t count, loff_t off)
return 0; return 0;
udev = to_usb_device (dev); udev = to_usb_device (dev);
len = usb_string(udev, udev->descriptor.iManufacturer, buf, PAGE_SIZE); len = usb_string(udev, udev->descriptor.iManufacturer, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n'; buf[len] = '\n';
buf[len+1] = 0x00; buf[len+1] = 0;
return len+1; return len+1;
} }
static DEVICE_ATTR(manufacturer,"manufacturer",S_IRUGO,show_manufacturer,NULL); static DEVICE_ATTR(manufacturer,"manufacturer",S_IRUGO,show_manufacturer,NULL);
...@@ -899,9 +903,11 @@ show_serial (struct device *dev, char *buf, size_t count, loff_t off) ...@@ -899,9 +903,11 @@ show_serial (struct device *dev, char *buf, size_t count, loff_t off)
return 0; return 0;
udev = to_usb_device (dev); udev = to_usb_device (dev);
len = usb_string(udev, udev->descriptor.iSerialNumber, buf, PAGE_SIZE); len = usb_string(udev, udev->descriptor.iSerialNumber, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n'; buf[len] = '\n';
buf[len+1] = 0x00; buf[len+1] = 0;
return len+1; return len+1;
} }
static DEVICE_ATTR(serial,"serial",S_IRUGO,show_serial,NULL); static DEVICE_ATTR(serial,"serial",S_IRUGO,show_serial,NULL);
...@@ -918,13 +924,13 @@ static void usb_find_drivers(struct usb_device *dev) ...@@ -918,13 +924,13 @@ static void usb_find_drivers(struct usb_device *dev)
unsigned claimed = 0; unsigned claimed = 0;
/* FIXME should get called for each new configuration not just the /* FIXME should get called for each new configuration not just the
* first one for a device. switching configs (or altesettings) should * first one for a device. switching configs (or altsettings) should
* undo driverfs and HCD state for the previous interfaces. * undo driverfs and HCD state for the previous interfaces.
*/ */
for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) { for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
struct usb_interface *interface = &dev->actconfig->interface[ifnum]; struct usb_interface *interface = &dev->actconfig->interface[ifnum];
struct usb_interface_descriptor *desc = interface->altsetting; struct usb_interface_descriptor *desc = interface->altsetting;
/* register this interface with driverfs */ /* register this interface with driverfs */
interface->dev.parent = &dev->dev; interface->dev.parent = &dev->dev;
interface->dev.bus = &usb_bus_type; interface->dev.bus = &usb_bus_type;
...@@ -1455,6 +1461,152 @@ int usb_new_device(struct usb_device *dev) ...@@ -1455,6 +1461,152 @@ int usb_new_device(struct usb_device *dev)
} }
/**
* usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_DMA_MAP
* @dev: device the buffer will be used with
* @size: requested buffer size
* @mem_flags: affect whether allocation may block
* @dma: used to return DMA address of buffer
*
* Return value is either null (indicating no buffer could be allocated), or
* the cpu-space pointer to a buffer that may be used to perform DMA to the
* specified device. Such cpu-space buffers are returned along with the DMA
* address (through the pointer provided).
*
* These buffers are used with URB_NO_DMA_MAP set in urb->transfer_flags to
* avoid behaviors like using "DMA bounce buffers", or tying down I/O mapping
* hardware for long idle periods. The implementation varies between
* platforms, depending on details of how DMA will work to this device.
*
* When the buffer is no longer used, free it with usb_buffer_free().
*/
void *usb_buffer_alloc (
struct usb_device *dev,
size_t size,
int mem_flags,
dma_addr_t *dma
)
{
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
return 0;
return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
}
/**
* usb_buffer_free - free memory allocated with usb_buffer_alloc()
* @dev: device the buffer was used with
* @size: requested buffer size
* @addr: CPU address of buffer
* @dma: DMA address of buffer
*
* This reclaims an I/O buffer, letting it be reused. The memory must have
* been allocated using usb_buffer_alloc(), and the parameters must match
* those provided in that allocation request.
*/
void usb_buffer_free (
struct usb_device *dev,
size_t size,
void *addr,
dma_addr_t dma
)
{
if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
return;
dev->bus->op->buffer_free (dev->bus, size, addr, dma);
}
/**
* usb_buffer_map - create DMA mapping(s) for an urb
* @urb: urb whose transfer_buffer will be mapped
*
* Return value is either null (indicating no buffer could be mapped), or
* the parameter. URB_NO_DMA_MAP is added to urb->transfer_flags if the
* operation succeeds.
*
* This call would normally be used for an urb which is reused, perhaps
* as the target of a large periodic transfer, with usb_buffer_dmasync()
* calls to synchronize memory and dma state. It may not be used for
* control requests.
*
* Reverse the effect of this call with usb_buffer_unmap().
*/
struct urb *usb_buffer_map (struct urb *urb)
{
struct usb_bus *bus;
struct usb_operations *op;
if (!urb
|| usb_pipecontrol (urb->pipe)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(op = bus->op)
|| !op->buffer_map)
return 0;
if (op->buffer_map (bus,
urb->transfer_buffer,
&urb->transfer_dma,
urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? USB_DIR_IN
: USB_DIR_OUT))
return 0;
urb->transfer_flags |= URB_NO_DMA_MAP;
return urb;
}
/**
* usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)
* @urb: urb whose transfer_buffer will be synchronized
*/
void usb_buffer_dmasync (struct urb *urb)
{
struct usb_bus *bus;
struct usb_operations *op;
if (!urb
|| !(urb->transfer_flags & URB_NO_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(op = bus->op)
|| !op->buffer_dmasync)
return;
op->buffer_dmasync (bus,
urb->transfer_dma,
urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? USB_DIR_IN
: USB_DIR_OUT);
}
/**
* usb_buffer_unmap - free DMA mapping(s) for an urb
* @urb: urb whose transfer_buffer will be unmapped
*
* Reverses the effect of usb_buffer_map().
*/
void usb_buffer_unmap (struct urb *urb)
{
struct usb_bus *bus;
struct usb_operations *op;
if (!urb
|| !(urb->transfer_flags & URB_NO_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(op = bus->op)
|| !op->buffer_unmap)
return;
op->buffer_unmap (bus,
urb->transfer_dma,
urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? USB_DIR_IN
: USB_DIR_OUT);
}
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
struct list_head *usb_driver_get_list(void) struct list_head *usb_driver_get_list(void)
{ {
...@@ -1534,4 +1686,11 @@ EXPORT_SYMBOL(__usb_get_extra_descriptor); ...@@ -1534,4 +1686,11 @@ EXPORT_SYMBOL(__usb_get_extra_descriptor);
EXPORT_SYMBOL(usb_get_current_frame_number); EXPORT_SYMBOL(usb_get_current_frame_number);
EXPORT_SYMBOL (usb_buffer_alloc);
EXPORT_SYMBOL (usb_buffer_free);
EXPORT_SYMBOL (usb_buffer_map);
EXPORT_SYMBOL (usb_buffer_dmasync);
EXPORT_SYMBOL (usb_buffer_unmap);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* /*
* Copyright (c) 2001 by David Brownell * Copyright (c) 2001-2002 by David Brownell
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
...@@ -175,3 +175,215 @@ dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {} ...@@ -175,3 +175,215 @@ dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
(status & PORT_CONNECT) ? " CONNECT" : "" \ (status & PORT_CONNECT) ? " CONNECT" : "" \
) )
#ifdef DEBUG
#define speed_char(info1) ({ char tmp; \
switch (info1 & (3 << 12)) { \
case 0 << 12: tmp = 'f'; break; \
case 1 << 12: tmp = 'l'; break; \
case 2 << 12: tmp = 'h'; break; \
default: tmp = '?'; break; \
}; tmp; })
static ssize_t
show_async (struct device *dev, char *buf, size_t count, loff_t off)
{
struct pci_dev *pdev;
struct ehci_hcd *ehci;
unsigned long flags;
unsigned temp, size;
char *next;
struct ehci_qh *qh;
if (off != 0)
return 0;
pdev = container_of (dev, struct pci_dev, dev);
ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
next = buf;
size = count;
/* dumps a snapshot of the async schedule.
* usually empty except for long-term bulk reads, or head.
* one QH per line, and TDs we know about
*/
spin_lock_irqsave (&ehci->lock, flags);
if (ehci->async) {
qh = ehci->async;
do {
u32 scratch;
struct list_head *entry;
struct ehci_qtd *td;
scratch = cpu_to_le32p (&qh->hw_info1);
temp = snprintf (next, size, "qh %p dev%d %cs ep%d",
qh, scratch & 0x007f,
speed_char (scratch),
(scratch >> 8) & 0x000f);
size -= temp;
next += temp;
list_for_each (entry, &qh->qtd_list) {
td = list_entry (entry, struct ehci_qtd,
qtd_list);
scratch = cpu_to_le32p (&td->hw_token);
temp = snprintf (next, size,
", td %p len=%d %s",
td, scratch >> 16,
({ char *tmp;
switch ((scratch>>8)&0x03) {
case 0: tmp = "out"; break;
case 1: tmp = "in"; break;
case 2: tmp = "setup"; break;
default: tmp = "?"; break;
} tmp;})
);
size -= temp;
next += temp;
}
temp = snprintf (next, size, "\n");
size -= temp;
next += temp;
} while ((qh = qh->qh_next.qh) != ehci->async);
}
spin_unlock_irqrestore (&ehci->lock, flags);
return count - size;
}
static DEVICE_ATTR (async, "sched-async", S_IRUSR, show_async, NULL);
#define DBG_SCHED_LIMIT 64
static ssize_t
show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
{
struct pci_dev *pdev;
struct ehci_hcd *ehci;
unsigned long flags;
union ehci_shadow p, *seen;
unsigned temp, size, seen_count;
char *next;
unsigned i, tag;
if (off != 0)
return 0;
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
return 0;
seen_count = 0;
pdev = container_of (dev, struct pci_dev, dev);
ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
next = buf;
size = count;
temp = snprintf (next, size, "size = %d\n", ehci->periodic_size);
size -= temp;
next += temp;
/* dump a snapshot of the periodic schedule.
* iso changes, interrupt usually doesn't.
*/
spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < ehci->periodic_size; i++) {
p = ehci->pshadow [i];
if (!p.ptr)
continue;
tag = Q_NEXT_TYPE (ehci->periodic [i]);
temp = snprintf (next, size, "%4d: ", i);
size -= temp;
next += temp;
do {
switch (tag) {
case Q_TYPE_QH:
temp = snprintf (next, size, " intr-%d %p",
p.qh->period, p.qh);
size -= temp;
next += temp;
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp].ptr == p.ptr)
break;
}
/* show more info the first time around */
if (temp == seen_count) {
u32 scratch = cpu_to_le32p (
&p.qh->hw_info1);
temp = snprintf (next, size,
" (%cs dev%d ep%d)",
speed_char (scratch),
scratch & 0x007f,
(scratch >> 8) & 0x000f);
/* FIXME TDs too */
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++].qh = p.qh;
} else
temp = 0;
tag = Q_NEXT_TYPE (p.qh->hw_next);
p = p.qh->qh_next;
break;
case Q_TYPE_FSTN:
temp = snprintf (next, size,
" fstn-%8x/%p", p.fstn->hw_prev,
p.fstn);
tag = Q_NEXT_TYPE (p.fstn->hw_next);
p = p.fstn->fstn_next;
break;
case Q_TYPE_ITD:
temp = snprintf (next, size,
" itd/%p", p.itd);
tag = Q_NEXT_TYPE (p.itd->hw_next);
p = p.itd->itd_next;
break;
case Q_TYPE_SITD:
temp = snprintf (next, size,
" sitd/%p", p.sitd);
tag = Q_NEXT_TYPE (p.sitd->hw_next);
p = p.sitd->sitd_next;
break;
}
size -= temp;
next += temp;
} while (p.ptr);
temp = snprintf (next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ehci->lock, flags);
kfree (seen);
return count - size;
}
static DEVICE_ATTR (periodic, "sched-periodic", S_IRUSR, show_periodic, NULL);
#undef DBG_SCHED_LIMIT
static inline void create_debug_files (struct ehci_hcd *bus)
{
device_create_file (&bus->hcd.pdev->dev, &dev_attr_async);
device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
}
static inline void remove_debug_files (struct ehci_hcd *bus)
{
device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async);
device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
}
#else /* DEBUG */
static inline void create_debug_files (struct ehci_hcd *bus)
{
}
static inline void remove_debug_files (struct ehci_hcd *bus)
{
}
#endif /* DEBUG */
...@@ -65,6 +65,8 @@ ...@@ -65,6 +65,8 @@
* *
* HISTORY: * HISTORY:
* *
* 2002-08-06 Handling for bulk and interrupt transfers is mostly shared;
* only scheduling is different, no arbitrary limitations.
* 2002-07-25 Sanity check PCI reads, mostly for better cardbus support, * 2002-07-25 Sanity check PCI reads, mostly for better cardbus support,
* clean up HC run state handshaking. * clean up HC run state handshaking.
* 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts * 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts
...@@ -85,7 +87,7 @@ ...@@ -85,7 +87,7 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4 * 2001-June Works with usb-storage and NEC EHCI on 2.4
*/ */
#define DRIVER_VERSION "2002-Jul-25" #define DRIVER_VERSION "2002-Aug-06"
#define DRIVER_AUTHOR "David Brownell" #define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
...@@ -93,6 +95,8 @@ ...@@ -93,6 +95,8 @@
// #define EHCI_VERBOSE_DEBUG // #define EHCI_VERBOSE_DEBUG
// #define have_split_iso // #define have_split_iso
#define INTR_AUTOMAGIC /* to be removed later in 2.5 */
/* magic numbers that can affect system performance */ /* magic numbers that can affect system performance */
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
#define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */ #define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */
...@@ -376,6 +380,8 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -376,6 +380,8 @@ static int ehci_start (struct usb_hcd *hcd)
return -ENOMEM; return -ENOMEM;
} }
create_debug_files (ehci);
/* /*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
* are explicitly handed to companion controller(s), so no TT is * are explicitly handed to companion controller(s), so no TT is
...@@ -429,6 +435,8 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -429,6 +435,8 @@ static void ehci_stop (struct usb_hcd *hcd)
ehci_ready (ehci); ehci_ready (ehci);
ehci_reset (ehci); ehci_reset (ehci);
remove_debug_files (ehci);
/* root hub is shut down separately (first, when possible) */ /* root hub is shut down separately (first, when possible) */
tasklet_disable (&ehci->tasklet); tasklet_disable (&ehci->tasklet);
ehci_tasklet ((unsigned long) ehci); ehci_tasklet ((unsigned long) ehci);
...@@ -614,7 +622,8 @@ static void ehci_irq (struct usb_hcd *hcd) ...@@ -614,7 +622,8 @@ static void ehci_irq (struct usb_hcd *hcd)
* *
* hcd-specific init for hcpriv hasn't been done yet * hcd-specific init for hcpriv hasn't been done yet
* *
* NOTE: EHCI queues control and bulk requests transparently, like OHCI. * NOTE: control, bulk, and interrupt share the same code to append TDs
* to a (possibly active) QH, and the same QH scanning code.
*/ */
static int ehci_urb_enqueue ( static int ehci_urb_enqueue (
struct usb_hcd *hcd, struct usb_hcd *hcd,
...@@ -626,10 +635,11 @@ static int ehci_urb_enqueue ( ...@@ -626,10 +635,11 @@ static int ehci_urb_enqueue (
urb->transfer_flags &= ~EHCI_STATE_UNLINK; urb->transfer_flags &= ~EHCI_STATE_UNLINK;
INIT_LIST_HEAD (&qtd_list); INIT_LIST_HEAD (&qtd_list);
switch (usb_pipetype (urb->pipe)) {
case PIPE_CONTROL: switch (usb_pipetype (urb->pipe)) {
case PIPE_BULK: // case PIPE_CONTROL:
// case PIPE_BULK:
default:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
return -ENOMEM; return -ENOMEM;
return submit_async (ehci, urb, &qtd_list, mem_flags); return submit_async (ehci, urb, &qtd_list, mem_flags);
...@@ -649,9 +659,6 @@ static int ehci_urb_enqueue ( ...@@ -649,9 +659,6 @@ static int ehci_urb_enqueue (
dbg ("no split iso support yet"); dbg ("no split iso support yet");
return -ENOSYS; return -ENOSYS;
#endif /* have_split_iso */ #endif /* have_split_iso */
default: /* can't happen */
return -ENOSYS;
} }
} }
...@@ -665,15 +672,16 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -665,15 +672,16 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
unsigned long flags; unsigned long flags;
dbg ("%s urb_dequeue %p qh state %d", dbg ("%s urb_dequeue %p qh %p state %d",
hcd->self.bus_name, urb, qh->qh_state); hcd->self.bus_name, urb, qh, qh->qh_state);
switch (usb_pipetype (urb->pipe)) { switch (usb_pipetype (urb->pipe)) {
case PIPE_CONTROL: // case PIPE_CONTROL:
case PIPE_BULK: // case PIPE_BULK:
default:
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
if (ehci->reclaim) { if (ehci->reclaim) {
dbg ("dq: reclaim busy, %s", RUN_CONTEXT); dbg ("dq: reclaim busy, %s", RUN_CONTEXT);
if (in_interrupt ()) { if (in_interrupt ()) {
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return -EAGAIN; return -EAGAIN;
...@@ -683,28 +691,43 @@ dbg ("dq: reclaim busy, %s", RUN_CONTEXT); ...@@ -683,28 +691,43 @@ dbg ("dq: reclaim busy, %s", RUN_CONTEXT);
&& ehci->hcd.state != USB_STATE_HALT && ehci->hcd.state != USB_STATE_HALT
) { ) {
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
// yeech ... this could spin for up to two frames! /* let pending unlinks complete */
dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d", wait_ms (1);
qh->qh_state, ehci->reclaim, ehci->hcd.state
);
udelay (100);
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
} }
} }
if (qh->qh_state == QH_STATE_LINKED) if (qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh); start_unlink_async (ehci, qh);
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return 0; break;
case PIPE_INTERRUPT: case PIPE_INTERRUPT:
intr_deschedule (ehci, urb->start_frame, qh, if (qh->qh_state == QH_STATE_LINKED) {
(urb->dev->speed == USB_SPEED_HIGH) /* messy, can spin or block a microframe ... */
? urb->interval intr_deschedule (ehci, qh, 1);
: (urb->interval << 3)); /* qh_state == IDLE */
if (ehci->hcd.state == USB_STATE_HALT) }
urb->status = -ESHUTDOWN; qh_completions (ehci, qh);
qh_completions (ehci, qh, 1);
return 0; /* reschedule QH iff another request is queued */
if (!list_empty (&qh->qtd_list)
&& HCD_IS_RUNNING (ehci->hcd.state)) {
int status;
spin_lock_irqsave (&ehci->lock, flags);
status = qh_schedule (ehci, qh);
spin_unlock_irqrestore (&ehci->lock, flags);
if (status != 0) {
// shouldn't happen often, but ...
// FIXME kill those tds' urbs
err ("can't reschedule qh %p, err %d",
qh, status);
}
return status;
}
break;
case PIPE_ISOCHRONOUS: case PIPE_ISOCHRONOUS:
// itd or sitd ... // itd or sitd ...
...@@ -712,9 +735,9 @@ dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d", ...@@ -712,9 +735,9 @@ dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d",
// wait till next completion, do it then. // wait till next completion, do it then.
// completion irqs can wait up to 1024 msec, // completion irqs can wait up to 1024 msec,
urb->transfer_flags |= EHCI_STATE_UNLINK; urb->transfer_flags |= EHCI_STATE_UNLINK;
return 0; break;
} }
return -EINVAL; return 0;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -728,6 +751,7 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -728,6 +751,7 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
int i; int i;
unsigned long flags; unsigned long flags;
/* ASSERT: no requests/urbs are still linked (so no TDs) */
/* ASSERT: nobody can be submitting urbs for this any more */ /* ASSERT: nobody can be submitting urbs for this any more */
dbg ("%s: free_config devnum %d", hcd->self.bus_name, udev->devnum); dbg ("%s: free_config devnum %d", hcd->self.bus_name, udev->devnum);
...@@ -736,34 +760,57 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -736,34 +760,57 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
if (dev->ep [i]) { if (dev->ep [i]) {
struct ehci_qh *qh; struct ehci_qh *qh;
char *why;
/* dev->ep never has ITDs or SITDs */ /* dev->ep never has ITDs or SITDs */
qh = (struct ehci_qh *) dev->ep [i]; qh = (struct ehci_qh *) dev->ep [i];
vdbg ("free_config, ep 0x%02x qh %p", i, qh);
if (!list_empty (&qh->qtd_list)) { /* detect/report non-recoverable errors */
dbg ("ep 0x%02x qh %p not empty!", i, qh); if (in_interrupt ())
why = "disconnect() didn't";
else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0
&& qh->qh_state != QH_STATE_IDLE)
why = "(active periodic)";
else
why = 0;
if (why) {
err ("dev %s-%s ep %d-%s error: %s",
hcd->self.bus_name, udev->devpath,
i & 0xf, (i & 0x10) ? "IN" : "OUT",
why);
BUG (); BUG ();
} }
dev->ep [i] = 0;
/* wait_ms() won't spin here -- we're a thread */ dev->ep [i] = 0;
if (qh->qh_state == QH_STATE_IDLE)
goto idle;
dbg ("free_config, async ep 0x%02x qh %p", i, qh);
/* scan_async() empties the ring as it does its work,
* using IAA, but doesn't (yet?) turn it off. if it
* doesn't empty this qh, likely it's the last entry.
*/
while (qh->qh_state == QH_STATE_LINKED while (qh->qh_state == QH_STATE_LINKED
&& ehci->reclaim && ehci->reclaim
&& ehci->hcd.state != USB_STATE_HALT && ehci->hcd.state != USB_STATE_HALT
) { ) {
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
/* wait_ms() won't spin, we're a thread;
* and we know IRQ+tasklet can progress
*/
wait_ms (1); wait_ms (1);
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
} }
if (qh->qh_state == QH_STATE_LINKED) { if (qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh); start_unlink_async (ehci, qh);
while (qh->qh_state != QH_STATE_IDLE) { while (qh->qh_state != QH_STATE_IDLE
spin_unlock_irqrestore (&ehci->lock, && ehci->hcd.state != USB_STATE_HALT) {
flags); spin_unlock_irqrestore (&ehci->lock,
wait_ms (1); flags);
spin_lock_irqsave (&ehci->lock, flags); wait_ms (1);
} spin_lock_irqsave (&ehci->lock, flags);
} }
idle:
qh_put (ehci, qh); qh_put (ehci, qh);
} }
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -50,7 +50,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -50,7 +50,7 @@ struct ehci_hcd { /* one per controller */
union ehci_shadow *pshadow; /* mirror hw periodic table */ union ehci_shadow *pshadow; /* mirror hw periodic table */
int next_uframe; /* scan periodic, start here */ int next_uframe; /* scan periodic, start here */
unsigned periodic_urbs; /* how many urbs scheduled? */ unsigned periodic_sched; /* periodic activity count */
/* deferred work from IRQ, etc */ /* deferred work from IRQ, etc */
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
...@@ -72,7 +72,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -72,7 +72,7 @@ struct ehci_hcd { /* one per controller */
}; };
/* unwrap an HCD pointer to get an EHCI_HCD pointer */ /* unwrap an HCD pointer to get an EHCI_HCD pointer */
#define hcd_to_ehci(hcd_ptr) list_entry(hcd_ptr, struct ehci_hcd, hcd) #define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd)
/* NOTE: urb->transfer_flags expected to not use this bit !!! */ /* NOTE: urb->transfer_flags expected to not use this bit !!! */
#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ #define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */
...@@ -287,12 +287,20 @@ struct ehci_qh { ...@@ -287,12 +287,20 @@ struct ehci_qh {
struct list_head qtd_list; /* sw qtd list */ struct list_head qtd_list; /* sw qtd list */
atomic_t refcount; atomic_t refcount;
unsigned short usecs; /* intr bandwidth */
unsigned short c_usecs; /* ... split completion bw */ u8 qh_state;
short qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */
/* periodic schedule info */
u8 usecs; /* intr bandwidth */
u8 gap_uf; /* uframes split/csplit gap */
u8 c_usecs; /* ... split completion bw */
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
} __attribute__ ((aligned (32))); } __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
...@@ -938,7 +938,7 @@ static void finish_unlinks (struct ohci_hcd *ohci, u16 tick) ...@@ -938,7 +938,7 @@ static void finish_unlinks (struct ohci_hcd *ohci, u16 tick)
/* ED's now officially unlinked, hc doesn't see */ /* ED's now officially unlinked, hc doesn't see */
ed->state = ED_IDLE; ed->state = ED_IDLE;
ed->hwINFO &= ~ED_SKIP; ed->hwINFO &= ~ED_SKIP;
ed->hwHeadP &= ~cpu_to_le32 (ED_H); ed->hwHeadP &= ~ED_H;
ed->hwNextED = 0; ed->hwNextED = 0;
/* but if there's work queued, reschedule */ /* but if there's work queued, reschedule */
......
...@@ -147,7 +147,8 @@ static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) ...@@ -147,7 +147,8 @@ static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
srb->host_scribble = (unsigned char *)us; srb->host_scribble = (unsigned char *)us;
/* enqueue the command */ /* enqueue the command */
BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE || us->srb != NULL); BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE);
BUG_ON(us->srb != NULL);
srb->scsi_done = done; srb->scsi_done = done;
us->srb = srb; us->srb = srb;
......
...@@ -734,6 +734,7 @@ extern void usb_deregister_dev(int num_minors, int start_minor); ...@@ -734,6 +734,7 @@ extern void usb_deregister_dev(int num_minors, int start_minor);
*/ */
#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */ #define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */
#define USB_ISO_ASAP 0x0002 /* iso-only, urb->start_frame ignored */ #define USB_ISO_ASAP 0x0002 /* iso-only, urb->start_frame ignored */
#define URB_NO_DMA_MAP 0x0004 /* urb->*_dma are valid on submit */
#define USB_ASYNC_UNLINK 0x0008 /* usb_unlink_urb() returns asap */ #define USB_ASYNC_UNLINK 0x0008 /* usb_unlink_urb() returns asap */
#define USB_NO_FSBR 0x0020 /* UHCI-specific */ #define USB_NO_FSBR 0x0020 /* UHCI-specific */
#define USB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet */ #define USB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet */
...@@ -771,11 +772,15 @@ typedef void (*usb_complete_t)(struct urb *); ...@@ -771,11 +772,15 @@ typedef void (*usb_complete_t)(struct urb *);
* @transfer_flags: A variety of flags may be used to affect how URB * @transfer_flags: A variety of flags may be used to affect how URB
* submission, unlinking, or operation are handled. Different * submission, unlinking, or operation are handled. Different
* kinds of URB can use different flags. * kinds of URB can use different flags.
* @transfer_buffer: For non-iso transfers, this identifies the buffer * @transfer_buffer: This identifies the buffer to (or from) which
* to (or from) which the I/O request will be performed. This * the I/O request will be performed (unless URB_NO_DMA_MAP is set).
* buffer must be suitable for DMA; allocate it with kmalloc() * This buffer must be suitable for DMA; allocate it with kmalloc()
* or equivalent. For transfers to "in" endpoints, contents of * or equivalent. For transfers to "in" endpoints, contents of
* this buffer will be modified. * this buffer will be modified. This buffer is used for data
* phases of control transfers.
* @transfer_dma: When transfer_flags includes URB_NO_DMA_MAP, the device
* driver is saying that it provided this DMA address, which the host
* controller driver should use instead of the transfer_buffer.
* @transfer_buffer_length: How big is transfer_buffer. The transfer may * @transfer_buffer_length: How big is transfer_buffer. The transfer may
* be broken up into chunks according to the current maximum packet * be broken up into chunks according to the current maximum packet
* size for the endpoint, which is a function of the configuration * size for the endpoint, which is a function of the configuration
...@@ -789,6 +794,11 @@ typedef void (*usb_complete_t)(struct urb *); ...@@ -789,6 +794,11 @@ typedef void (*usb_complete_t)(struct urb *);
* @setup_packet: Only used for control transfers, this points to eight bytes * @setup_packet: Only used for control transfers, this points to eight bytes
* of setup data. Control transfers always start by sending this data * of setup data. Control transfers always start by sending this data
* to the device. Then transfer_buffer is read or written, if needed. * to the device. Then transfer_buffer is read or written, if needed.
* (Not used when URB_NO_DMA_MAP is set.)
* @setup_dma: For control transfers with URB_NO_DMA_MAP set, the device
* driver has provided this DMA address for the setup packet. The
* host controller driver should use instead of setup_buffer.
* If there is a data phase, its buffer is identified by transfer_dma.
* @start_frame: Returns the initial frame for interrupt or isochronous * @start_frame: Returns the initial frame for interrupt or isochronous
* transfers. * transfers.
* @number_of_packets: Lists the number of ISO transfer buffers. * @number_of_packets: Lists the number of ISO transfer buffers.
...@@ -811,6 +821,23 @@ typedef void (*usb_complete_t)(struct urb *); ...@@ -811,6 +821,23 @@ typedef void (*usb_complete_t)(struct urb *);
* are submitted using usb_submit_urb(), and pending requests may be canceled * are submitted using usb_submit_urb(), and pending requests may be canceled
* using usb_unlink_urb(). * using usb_unlink_urb().
* *
* Data Transfer Buffers:
*
* Normally drivers provide I/O buffers allocated with kmalloc() or otherwise
* taken from the general page pool. That is provided by transfer_buffer
* (control requests also use setup_packet), and host controller drivers
* perform a dma mapping (and unmapping) for each buffer transferred. Those
* mapping operations can be expensive on some platforms (such using a dma
* bounce buffer), although they're cheap on commodity x86 and ppc hardware.
*
* Alternatively, drivers may pass the URB_NO_DMA_MAP transfer flag, which
* tells the host controller driver that no such mapping is needed since
* the device driver is DMA-aware. For example, they might allocate a DMA
* buffer with usb_buffer_alloc(), or call usb_buffer_map().
* When this transfer flag is provided, host controller drivers will use the
* dma addresses found in the transfer_dma and/or setup_dma fields rather than
* determing a dma address themselves.
*
* Initialization: * Initialization:
* *
* All URBs submitted must initialize dev, pipe, * All URBs submitted must initialize dev, pipe,
...@@ -818,10 +845,10 @@ typedef void (*usb_complete_t)(struct urb *); ...@@ -818,10 +845,10 @@ typedef void (*usb_complete_t)(struct urb *);
* The USB_ASYNC_UNLINK transfer flag affects later invocations of * The USB_ASYNC_UNLINK transfer flag affects later invocations of
* the usb_unlink_urb() routine. * the usb_unlink_urb() routine.
* *
* All non-isochronous URBs must also initialize * All URBs must also initialize
* transfer_buffer and transfer_buffer_length. They may provide the * transfer_buffer and transfer_buffer_length. They may provide the
* URB_SHORT_NOT_OK transfer flag, indicating that short reads are * URB_SHORT_NOT_OK transfer flag, indicating that short reads are
* to be treated as errors. * to be treated as errors; that flag is invalid for write requests.
* *
* Bulk URBs may * Bulk URBs may
* use the USB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers * use the USB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers
...@@ -896,10 +923,12 @@ struct urb ...@@ -896,10 +923,12 @@ struct urb
int status; /* (return) non-ISO status */ int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */ void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
int transfer_buffer_length; /* (in) data buffer length */ int transfer_buffer_length; /* (in) data buffer length */
int actual_length; /* (return) actual transfer length */ int actual_length; /* (return) actual transfer length */
int bandwidth; /* bandwidth for INT/ISO request */ int bandwidth; /* bandwidth for INT/ISO request */
unsigned char *setup_packet; /* (in) setup packet (control only) */ unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (INT/ISO) */ int start_frame; /* (modify) start frame (INT/ISO) */
int number_of_packets; /* (in) number of ISO packets */ int number_of_packets; /* (in) number of ISO packets */
int interval; /* (in) transfer interval (INT/ISO) */ int interval; /* (in) transfer interval (INT/ISO) */
...@@ -910,6 +939,8 @@ struct urb ...@@ -910,6 +939,8 @@ struct urb
struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */ struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */
}; };
/* -------------------------------------------------------------------------- */
/** /**
* usb_fill_control_urb - initializes a control urb * usb_fill_control_urb - initializes a control urb
* @urb: pointer to the urb to initialize. * @urb: pointer to the urb to initialize.
...@@ -1032,6 +1063,16 @@ extern struct urb *usb_get_urb(struct urb *urb); ...@@ -1032,6 +1063,16 @@ extern struct urb *usb_get_urb(struct urb *urb);
extern int usb_submit_urb(struct urb *urb, int mem_flags); extern int usb_submit_urb(struct urb *urb, int mem_flags);
extern int usb_unlink_urb(struct urb *urb); extern int usb_unlink_urb(struct urb *urb);
#define HAVE_USB_BUFFERS
void *usb_buffer_alloc (struct usb_device *dev, size_t size,
int mem_flags, dma_addr_t *dma);
void usb_buffer_free (struct usb_device *dev, size_t size,
void *addr, dma_addr_t dma);
struct urb *usb_buffer_map (struct urb *urb);
void usb_buffer_dmasync (struct urb *urb);
void usb_buffer_unmap (struct urb *urb);
/*-------------------------------------------------------------------* /*-------------------------------------------------------------------*
* SYNCHRONOUS CALL SUPPORT * * SYNCHRONOUS CALL SUPPORT *
*-------------------------------------------------------------------*/ *-------------------------------------------------------------------*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment