summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <greg@kroah.com>2002-08-06 08:02:34 -0700
committerGreg Kroah-Hartman <greg@kroah.com>2002-08-06 08:02:34 -0700
commitb2fb8f66697bfa6223ec451dd5fe0bebde62ac4b (patch)
treed221f31d60047fb0b3f8d93641e9bb9f232e3afb
parent6865a163448f42fb7187ddbcb7df58ed4e538977 (diff)
parent7cbe5bbe6bb76f787a6ff8da3e38ab9cbd15583d (diff)
Merge kroah.com:/home/linux/linux/BK/bleeding-2.5
into kroah.com:/home/linux/linux/BK/gregkh-2.5
-rw-r--r--Documentation/DocBook/kernel-api.tmpl2
-rw-r--r--drivers/usb/core/Makefile4
-rw-r--r--drivers/usb/core/buffer.c186
-rw-r--r--drivers/usb/core/hcd-pci.c17
-rw-r--r--drivers/usb/core/hcd.c12
-rw-r--r--drivers/usb/core/hcd.h41
-rw-r--r--drivers/usb/core/usb.c175
-rw-r--r--drivers/usb/host/ehci-dbg.c214
-rw-r--r--drivers/usb/host/ehci-hcd.c129
-rw-r--r--drivers/usb/host/ehci-q.c356
-rw-r--r--drivers/usb/host/ehci-sched.c416
-rw-r--r--drivers/usb/host/ehci.h18
-rw-r--r--drivers/usb/host/ohci-q.c2
-rw-r--r--drivers/usb/storage/scsiglue.c3
-rw-r--r--include/linux/usb.h53
15 files changed, 1168 insertions, 460 deletions
diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl
index 39a9c8aee6ee..129bbbb7d246 100644
--- a/Documentation/DocBook/kernel-api.tmpl
+++ b/Documentation/DocBook/kernel-api.tmpl
@@ -299,6 +299,8 @@ KAO -->
EHCI, OHCI, or UHCI.
</para>
!Edrivers/usb/core/hcd.c
+!Edrivers/usb/core/hcd-pci.c
+!Edrivers/usb/core/buffer.c
</sect1>
</chapter>
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 446a76045b82..87c3989fc246 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -2,10 +2,10 @@
# 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 \
- config.o file.o
+ config.o file.o buffer.o
ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
new file mode 100644
index 000000000000..097a5b51052d
--- /dev/null
+++ b/drivers/usb/core/buffer.c
@@ -0,0 +1,186 @@
+/*
+ * 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
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 446e7870c348..a1019b4b838b 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -130,10 +130,19 @@ clean_2:
return retval;
}
}
- pci_set_drvdata(dev, hcd);
+ pci_set_drvdata (dev, hcd);
hcd->driver = driver;
hcd->description = driver->description;
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);
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
@@ -154,8 +163,7 @@ clean_2:
!= 0) {
err ("request interrupt %s failed", bufp);
retval = -EBUSY;
- driver->hcd_free (hcd);
- goto clean_2;
+ goto clean_3;
}
hcd->irq = dev->irq;
@@ -168,8 +176,6 @@ clean_2:
usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations;
hcd->self.hcpriv = (void *) hcd;
- hcd->self.bus_name = dev->slot_name;
- hcd->product_desc = dev->name;
INIT_LIST_HEAD (&hcd->dev_list);
@@ -216,6 +222,7 @@ void usb_hcd_pci_remove (struct pci_dev *dev)
usb_disconnect (&hub);
hcd->driver->stop (hcd);
+ hcd_buffer_destroy (hcd);
hcd->state = USB_STATE_HALT;
free_irq (hcd->irq, hcd);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 05bb930ea5fd..d568135857e2 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -454,7 +454,6 @@ static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb)
/* rh_timer protected by hcd_data_lock */
if (timer_pending (&hcd->rh_timer)
|| urb->status != -EINPROGRESS
- || !HCD_IS_RUNNING (hcd->state)
|| urb->transfer_buffer_length < len) {
dbg ("not queuing status urb, stat %d", urb->status);
return -EINVAL;
@@ -508,8 +507,12 @@ static void rh_report_status (unsigned long ptr)
BUG ();
}
spin_unlock_irqrestore (&hcd_data_lock, flags);
- } else
+ } else {
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 {
/* this urb's been unlinked */
urb->hcpriv = 0;
@@ -1245,6 +1248,11 @@ struct usb_operations usb_hcd_operations = {
.submit_urb = hcd_submit_urb,
.unlink_urb = hcd_unlink_urb,
.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);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 111f6cbd886f..e33a8b409ee4 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -58,6 +58,9 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
atomic_t resume_count; /* multiple resumes issue */
#endif
+#define HCD_BUFFER_POOLS 4
+ struct pci_pool *pool [HCD_BUFFER_POOLS];
+
int state;
# define __ACTIVE 0x01
# define __SLEEPY 0x02
@@ -109,6 +112,25 @@ struct usb_operations {
int (*get_frame_number) (struct usb_device *usb_dev);
int (*submit_urb) (struct urb *urb, int mem_flags);
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 */
@@ -181,6 +203,25 @@ extern int usb_hcd_pci_resume (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,
+ 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 */
extern struct usb_operations usb_hcd_operations;
extern void usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 17fe095d0d11..03fdc5368f7d 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -863,9 +863,11 @@ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t
return 0;
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+1] = 0x00;
+ buf[len+1] = 0;
return len+1;
}
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)
return 0;
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+1] = 0x00;
+ buf[len+1] = 0;
return len+1;
}
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)
return 0;
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+1] = 0x00;
+ buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(serial,"serial",S_IRUGO,show_serial,NULL);
@@ -918,13 +924,13 @@ static void usb_find_drivers(struct usb_device *dev)
unsigned claimed = 0;
/* 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.
*/
for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
struct usb_interface *interface = &dev->actconfig->interface[ifnum];
struct usb_interface_descriptor *desc = interface->altsetting;
-
+
/* register this interface with driverfs */
interface->dev.parent = &dev->dev;
interface->dev.bus = &usb_bus_type;
@@ -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
struct list_head *usb_driver_get_list(void)
{
@@ -1534,4 +1686,11 @@ EXPORT_SYMBOL(__usb_get_extra_descriptor);
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");
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 8b1f0135aa99..83a9a9be9916 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -1,5 +1,5 @@
/*
- * 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
* 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) {}
(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 */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index dcb10d5dc2cd..6aaf8a72f382 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -65,6 +65,8 @@
*
* 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,
* clean up HC run state handshaking.
* 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts
@@ -85,7 +87,7 @@
* 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_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
@@ -93,6 +95,8 @@
// #define EHCI_VERBOSE_DEBUG
// #define have_split_iso
+#define INTR_AUTOMAGIC /* to be removed later in 2.5 */
+
/* magic numbers that can affect system performance */
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
#define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */
@@ -376,6 +380,8 @@ done2:
return -ENOMEM;
}
+ create_debug_files (ehci);
+
/*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
* are explicitly handed to companion controller(s), so no TT is
@@ -429,6 +435,8 @@ static void ehci_stop (struct usb_hcd *hcd)
ehci_ready (ehci);
ehci_reset (ehci);
+ remove_debug_files (ehci);
+
/* root hub is shut down separately (first, when possible) */
tasklet_disable (&ehci->tasklet);
ehci_tasklet ((unsigned long) ehci);
@@ -614,7 +622,8 @@ dead:
*
* 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 (
struct usb_hcd *hcd,
@@ -626,10 +635,11 @@ static int ehci_urb_enqueue (
urb->transfer_flags &= ~EHCI_STATE_UNLINK;
INIT_LIST_HEAD (&qtd_list);
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_CONTROL:
- case PIPE_BULK:
+ switch (usb_pipetype (urb->pipe)) {
+ // case PIPE_CONTROL:
+ // case PIPE_BULK:
+ default:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
return -ENOMEM;
return submit_async (ehci, urb, &qtd_list, mem_flags);
@@ -649,9 +659,6 @@ static int ehci_urb_enqueue (
dbg ("no split iso support yet");
return -ENOSYS;
#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)
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
unsigned long flags;
- dbg ("%s urb_dequeue %p qh state %d",
- hcd->self.bus_name, urb, qh->qh_state);
+ dbg ("%s urb_dequeue %p qh %p state %d",
+ hcd->self.bus_name, urb, qh, qh->qh_state);
switch (usb_pipetype (urb->pipe)) {
- case PIPE_CONTROL:
- case PIPE_BULK:
+ // case PIPE_CONTROL:
+ // case PIPE_BULK:
+ default:
spin_lock_irqsave (&ehci->lock, flags);
if (ehci->reclaim) {
-dbg ("dq: reclaim busy, %s", RUN_CONTEXT);
+ dbg ("dq: reclaim busy, %s", RUN_CONTEXT);
if (in_interrupt ()) {
spin_unlock_irqrestore (&ehci->lock, flags);
return -EAGAIN;
@@ -683,28 +691,43 @@ dbg ("dq: reclaim busy, %s", RUN_CONTEXT);
&& ehci->hcd.state != USB_STATE_HALT
) {
spin_unlock_irqrestore (&ehci->lock, flags);
-// yeech ... this could spin for up to two frames!
-dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d",
- qh->qh_state, ehci->reclaim, ehci->hcd.state
-);
- udelay (100);
+ /* let pending unlinks complete */
+ wait_ms (1);
spin_lock_irqsave (&ehci->lock, flags);
}
}
if (qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh);
spin_unlock_irqrestore (&ehci->lock, flags);
- return 0;
+ break;
case PIPE_INTERRUPT:
- intr_deschedule (ehci, urb->start_frame, qh,
- (urb->dev->speed == USB_SPEED_HIGH)
- ? urb->interval
- : (urb->interval << 3));
- if (ehci->hcd.state == USB_STATE_HALT)
- urb->status = -ESHUTDOWN;
- qh_completions (ehci, qh, 1);
- return 0;
+ if (qh->qh_state == QH_STATE_LINKED) {
+ /* messy, can spin or block a microframe ... */
+ intr_deschedule (ehci, qh, 1);
+ /* qh_state == IDLE */
+ }
+ qh_completions (ehci, qh);
+
+ /* 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:
// itd or sitd ...
@@ -712,9 +735,9 @@ dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d",
// wait till next completion, do it then.
// completion irqs can wait up to 1024 msec,
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)
int i;
unsigned long flags;
+ /* ASSERT: no requests/urbs are still linked (so no TDs) */
/* ASSERT: nobody can be submitting urbs for this any more */
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)
for (i = 0; i < 32; i++) {
if (dev->ep [i]) {
struct ehci_qh *qh;
+ char *why;
/* dev->ep never has ITDs or SITDs */
qh = (struct ehci_qh *) dev->ep [i];
- vdbg ("free_config, ep 0x%02x qh %p", i, qh);
- if (!list_empty (&qh->qtd_list)) {
- dbg ("ep 0x%02x qh %p not empty!", i, qh);
+
+ /* detect/report non-recoverable errors */
+ 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 ();
}
- 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
&& ehci->reclaim
&& ehci->hcd.state != USB_STATE_HALT
) {
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);
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);
- while (qh->qh_state != QH_STATE_IDLE) {
- spin_unlock_irqrestore (&ehci->lock,
- flags);
- wait_ms (1);
- spin_lock_irqsave (&ehci->lock, flags);
- }
+ while (qh->qh_state != QH_STATE_IDLE
+ && ehci->hcd.state != USB_STATE_HALT) {
+ spin_unlock_irqrestore (&ehci->lock,
+ flags);
+ wait_ms (1);
+ spin_lock_irqsave (&ehci->lock, flags);
}
+idle:
qh_put (ehci, qh);
}
}
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 3057d75b0fed..e0426d675d60 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -47,9 +47,11 @@ static int
qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token)
{
int i, count;
+ u64 addr = buf;
/* one buffer entry per 4K ... first might be short or unaligned */
- qtd->hw_buf [0] = cpu_to_le32 (buf);
+ qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
+ qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
count = 0x1000 - (buf & 0x0fff); /* rest of that page */
if (likely (len < count)) /* ... iff needed */
count = len;
@@ -59,7 +61,7 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token)
/* per-qtd limit: from 16K to 20K (best alignment) */
for (i = 1; count < len && i < 5; i++) {
- u64 addr = buf;
+ addr = buf;
qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
buf += 0x1000;
@@ -157,30 +159,6 @@ static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
}
}
-static void ehci_urb_complete (
- struct ehci_hcd *ehci,
- dma_addr_t addr,
- struct urb *urb
-) {
- if (urb->transfer_buffer_length && usb_pipein (urb->pipe))
- pci_dma_sync_single (ehci->hcd.pdev, addr,
- urb->transfer_buffer_length,
- PCI_DMA_FROMDEVICE);
-
- /* cleanse status if we saw no error */
- if (likely (urb->status == -EINPROGRESS)) {
- if (urb->actual_length != urb->transfer_buffer_length
- && (urb->transfer_flags & URB_SHORT_NOT_OK))
- urb->status = -EREMOTEIO;
- else
- urb->status = 0;
- }
-
- /* only report unlinks once */
- if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN))
- urb->complete (urb);
-}
-
/* urb->lock ignored from here on (hcd is done with urb) */
static void ehci_urb_done (
@@ -188,6 +166,11 @@ static void ehci_urb_done (
dma_addr_t addr,
struct urb *urb
) {
+#ifdef INTR_AUTOMAGIC
+ struct urb *resubmit = 0;
+ struct usb_device *dev = 0;
+#endif
+
if (urb->transfer_buffer_length)
pci_unmap_single (ehci->hcd.pdev,
addr,
@@ -196,7 +179,23 @@ static void ehci_urb_done (
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
if (likely (urb->hcpriv != 0)) {
- qh_put (ehci, (struct ehci_qh *) urb->hcpriv);
+ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
+
+ /* S-mask in a QH means it's an interrupt urb */
+ if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) {
+
+ /* ... update hc-wide periodic stats (for usbfs) */
+ ehci->hcd.self.bandwidth_int_reqs--;
+
+#ifdef INTR_AUTOMAGIC
+ if (!((urb->status == -ENOENT)
+ || (urb->status == -ECONNRESET))) {
+ resubmit = usb_get_urb (urb);
+ dev = urb->dev;
+ }
+#endif
+ }
+ qh_put (ehci, qh);
urb->hcpriv = 0;
}
@@ -208,33 +207,46 @@ static void ehci_urb_done (
urb->status = 0;
}
- /* hand off urb ownership */
usb_hcd_giveback_urb (&ehci->hcd, urb);
+
+#ifdef INTR_AUTOMAGIC
+ if (resubmit && ((urb->status == -ENOENT)
+ || (urb->status == -ECONNRESET))) {
+ usb_put_urb (resubmit);
+ resubmit = 0;
+ }
+ // device drivers will soon be doing something like this
+ if (resubmit) {
+ int status;
+
+ resubmit->dev = dev;
+ status = usb_submit_urb (resubmit, SLAB_KERNEL);
+ if (status != 0)
+ err ("can't resubmit interrupt urb %p: status %d",
+ resubmit, status);
+ usb_put_urb (resubmit);
+ }
+#endif
}
/*
* Process completed qtds for a qh, issuing completions if needed.
- * When freeing: frees qtds, unmaps buf, returns URB to driver.
- * When not freeing (queued periodic qh): retain qtds, mapping, and urb.
+ * Frees qtds, unmaps buf, returns URB to driver.
* Races up to qh->hw_current; returns number of urb completions.
*/
-static int
-qh_completions (
- struct ehci_hcd *ehci,
- struct ehci_qh *qh,
- int freeing
-) {
+static void
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
struct ehci_qtd *qtd, *last;
struct list_head *next, *qtd_list = &qh->qtd_list;
int unlink = 0, halted = 0;
unsigned long flags;
- int retval = 0;
spin_lock_irqsave (&ehci->lock, flags);
if (unlikely (list_empty (qtd_list))) {
spin_unlock_irqrestore (&ehci->lock, flags);
- return retval;
+ return;
}
/* scan QTDs till end of list, or we reach an active one */
@@ -251,14 +263,8 @@ qh_completions (
if (likely (last->urb != urb)) {
/* complete() can reenter this HCD */
spin_unlock_irqrestore (&ehci->lock, flags);
- if (likely (freeing != 0))
- ehci_urb_done (ehci, last->buf_dma,
- last->urb);
- else
- ehci_urb_complete (ehci, last->buf_dma,
- last->urb);
+ ehci_urb_done (ehci, last->buf_dma, last->urb);
spin_lock_irqsave (&ehci->lock, flags);
- retval++;
}
/* qh overlays can have HC's old cached copies of
@@ -270,8 +276,7 @@ qh_completions (
qh->hw_qtd_next = last->hw_next;
}
- if (likely (freeing != 0))
- ehci_qtd_free (ehci, last);
+ ehci_qtd_free (ehci, last);
last = 0;
}
next = qtd->qtd_list.next;
@@ -288,7 +293,7 @@ qh_completions (
/* fault: unlink the rest, since this qtd saw an error? */
if (unlikely ((token & QTD_STS_HALT) != 0)) {
- freeing = unlink = 1;
+ unlink = 1;
/* status copied below */
/* QH halts only because of fault (above) or unlink (here). */
@@ -296,13 +301,14 @@ qh_completions (
/* unlinking everything because of HC shutdown? */
if (ehci->hcd.state == USB_STATE_HALT) {
- freeing = unlink = 1;
+ unlink = 1;
/* explicit unlink, maybe starting here? */
} else if (qh->qh_state == QH_STATE_IDLE
&& (urb->status == -ECONNRESET
+ || urb->status == -ESHUTDOWN
|| urb->status == -ENOENT)) {
- freeing = unlink = 1;
+ unlink = 1;
/* QH halted to unlink urbs _after_ this? */
} else if (!unlink && (token & QTD_STS_ACTIVE) != 0) {
@@ -312,7 +318,7 @@ qh_completions (
/* unlink the rest? once we start unlinking, after
* a fault or explicit unlink, we unlink all later
- * urbs. usb spec requires that.
+ * urbs. usb spec requires that for faults...
*/
if (unlink && urb->status == -EINPROGRESS)
urb->status = -ECONNRESET;
@@ -330,31 +336,7 @@ qh_completions (
qtd_copy_status (urb, qtd->length, token);
spin_unlock (&urb->lock);
- /*
- * NOTE: this won't work right with interrupt urbs that
- * need multiple qtds ... only the first scan of qh->qtd_list
- * starts at the right qtd, yet multiple scans could happen
- * for transfers that are scheduled across multiple uframes.
- * (Such schedules are not currently allowed!)
- */
- if (likely (freeing != 0))
- list_del (&qtd->qtd_list);
- else {
- /* restore everything the HC could change
- * from an interrupt QTD
- */
- qtd->hw_token = (qtd->hw_token
- & __constant_cpu_to_le32 (0x8300))
- | cpu_to_le32 (qtd->length << 16)
- | __constant_cpu_to_le32 (QTD_STS_ACTIVE
- | (EHCI_TUNE_CERR << 10));
- qtd->hw_buf [0] &= ~__constant_cpu_to_le32 (0x0fff);
-
- /* this offset, and the length above,
- * are likely wrong on QTDs #2..N
- */
- qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma);
- }
+ list_del (&qtd->qtd_list);
#if 0
if (urb->status == -EINPROGRESS)
@@ -382,14 +364,9 @@ qh_completions (
/* last urb's completion might still need calling */
if (likely (last != 0)) {
- if (likely (freeing != 0)) {
- ehci_urb_done (ehci, last->buf_dma, last->urb);
- ehci_qtd_free (ehci, last);
- } else
- ehci_urb_complete (ehci, last->buf_dma, last->urb);
- retval++;
+ ehci_urb_done (ehci, last->buf_dma, last->urb);
+ ehci_qtd_free (ehci, last);
}
- return retval;
}
/*-------------------------------------------------------------------------*/
@@ -450,6 +427,7 @@ qh_urb_transaction (
struct ehci_qtd *qtd, *qtd_prev;
dma_addr_t buf, map_buf;
int len, maxpacket;
+ int is_input;
u32 token;
/*
@@ -495,10 +473,11 @@ qh_urb_transaction (
* data transfer stage: buffer setup
*/
len = urb->transfer_buffer_length;
+ is_input = usb_pipein (urb->pipe);
if (likely (len > 0)) {
buf = map_buf = pci_map_single (ehci->hcd.pdev,
urb->transfer_buffer, len,
- usb_pipein (urb->pipe)
+ is_input
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
if (unlikely (!buf))
@@ -506,12 +485,11 @@ qh_urb_transaction (
} else
buf = map_buf = 0;
- if (!buf || usb_pipein (urb->pipe))
+ if (!buf || is_input)
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
- maxpacket = usb_maxpacket (urb->dev, urb->pipe,
- usb_pipeout (urb->pipe));
+ maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff;
/*
* buffer gets wrapped in one or more qtds;
@@ -607,6 +585,11 @@ clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
// That'd mean updating how usbcore talks to HCDs. (2.5?)
+// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+// ... and packet size, for any kind of endpoint descriptor
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff)
+
/*
* Each QH holds a qtd list; a QH is used for everything except iso.
*
@@ -624,6 +607,8 @@ ehci_qh_make (
) {
struct ehci_qh *qh = ehci_qh_alloc (ehci, flags);
u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
if (!qh)
return qh;
@@ -634,6 +619,53 @@ ehci_qh_make (
info1 |= usb_pipeendpoint (urb->pipe) << 8;
info1 |= usb_pipedevice (urb->pipe) << 0;
+ is_input = usb_pipein (urb->pipe);
+ type = usb_pipetype (urb->pipe);
+ maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
+
+ /* Compute interrupt scheduling parameters just once, and save.
+ * - allowing for high bandwidth, how many nsec/uframe are used?
+ * - split transactions need a second CSPLIT uframe; same question
+ * - splits also need a schedule gap (for full/low speed I/O)
+ * - qh has a polling interval
+ *
+ * For control/bulk requests, the HC or TT handles these.
+ */
+ if (type == PIPE_INTERRUPT) {
+ qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
+ hb_mult (maxp) * max_packet (maxp));
+ qh->start = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->c_usecs = 0;
+ qh->gap_uf = 0;
+
+ /* FIXME handle HS periods of less than 1 frame. */
+ qh->period = urb->interval >> 3;
+ if (qh->period < 1) {
+ dbg ("intr period %d uframes, NYET!",
+ urb->interval);
+ qh = 0;
+ goto done;
+ }
+ } else {
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
+ is_input, 0, maxp) / (125 * 1000);
+
+ /* FIXME this just approximates SPLIT/CSPLIT times */
+ if (is_input) { // SPLIT, gap, CSPLIT+DATA
+ qh->c_usecs = qh->usecs + HS_USECS (0);
+ qh->usecs = HS_USECS (1);
+ } else { // SPLIT+DATA, gap, CSPLIT
+ qh->usecs += HS_USECS (1);
+ qh->c_usecs = HS_USECS (0);
+ }
+
+ qh->period = urb->interval;
+ }
+ }
+
/* using TT? */
switch (urb->dev->speed) {
case USB_SPEED_LOW:
@@ -643,67 +675,63 @@ ehci_qh_make (
case USB_SPEED_FULL:
/* EPS 0 means "full" */
info1 |= (EHCI_TUNE_RL_TT << 28);
- if (usb_pipecontrol (urb->pipe)) {
+ if (type == PIPE_CONTROL) {
info1 |= (1 << 27); /* for TT */
info1 |= 1 << 14; /* toggle from qtd */
}
- info1 |= usb_maxpacket (urb->dev, urb->pipe,
- usb_pipeout (urb->pipe)) << 16;
+ info1 |= maxp << 16;
info2 |= (EHCI_TUNE_MULT_TT << 30);
info2 |= urb->dev->ttport << 23;
info2 |= urb->dev->tt->hub->devnum << 16;
- /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets c-mask }
- * ... and a 0.96 scheduler might use FSTN nodes too
- */
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
break;
case USB_SPEED_HIGH: /* no TT involved */
info1 |= (2 << 12); /* EPS "high" */
info1 |= (EHCI_TUNE_RL_HS << 28);
- if (usb_pipecontrol (urb->pipe)) {
+ if (type == PIPE_CONTROL) {
info1 |= 64 << 16; /* usb2 fixed maxpacket */
info1 |= 1 << 14; /* toggle from qtd */
info2 |= (EHCI_TUNE_MULT_HS << 30);
- } else if (usb_pipebulk (urb->pipe)) {
+ } else if (type == PIPE_BULK) {
info1 |= 512 << 16; /* usb2 fixed maxpacket */
info2 |= (EHCI_TUNE_MULT_HS << 30);
- } else {
- u32 temp;
- temp = usb_maxpacket (urb->dev, urb->pipe,
- usb_pipeout (urb->pipe));
- info1 |= (temp & 0x3ff) << 16; /* maxpacket */
- /* HS intr can be "high bandwidth" */
- temp = 1 + ((temp >> 11) & 0x03);
- info2 |= temp << 30; /* mult */
+ } else { /* PIPE_INTERRUPT */
+ info1 |= max_packet (maxp) << 16;
+ info2 |= hb_mult (maxp) << 30;
}
break;
- default:
#ifdef DEBUG
+ default:
BUG ();
#endif
}
- /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets s-mask } */
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
qh->qh_state = QH_STATE_IDLE;
qh->hw_info1 = cpu_to_le32 (info1);
qh->hw_info2 = cpu_to_le32 (info2);
/* initialize sw and hw queues with these qtds */
- list_splice (qtd_list, &qh->qtd_list);
- qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list));
+ if (!list_empty (qtd_list)) {
+ list_splice (qtd_list, &qh->qtd_list);
+ qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list));
+ } else {
+ qh->hw_qtd_next = qh->hw_alt_next = EHCI_LIST_END;
+ }
/* initialize data toggle state */
- if (!usb_pipecontrol (urb->pipe))
- clear_toggle (urb->dev,
- usb_pipeendpoint (urb->pipe),
- usb_pipeout (urb->pipe),
- qh);
+ clear_toggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, qh);
+done:
return qh;
}
+#undef hb_mult
+#undef hb_packet
/*-------------------------------------------------------------------------*/
@@ -745,50 +773,48 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
-static int
-submit_async (
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct ehci_qh *qh_append_tds (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list,
- int mem_flags
-) {
- struct ehci_qtd *qtd;
- struct hcd_dev *dev;
- int epnum;
- unsigned long flags;
+ int epnum,
+ void **ptr
+)
+{
struct ehci_qh *qh = 0;
- qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
- dev = (struct hcd_dev *)urb->dev->hcpriv;
- epnum = usb_pipeendpoint (urb->pipe);
- if (usb_pipein (urb->pipe))
- epnum |= 0x10;
-
- vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
- ehci->hcd.self.bus_name, urb, urb->transfer_buffer_length,
- epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
- qtd, dev ? dev->ep [epnum] : (void *)~0);
-
- spin_lock_irqsave (&ehci->lock, flags);
-
- qh = (struct ehci_qh *) dev->ep [epnum];
+ qh = (struct ehci_qh *) *ptr;
if (likely (qh != 0)) {
- u32 hw_next = QTD_NEXT (qtd->qtd_dma);
+ struct ehci_qtd *qtd;
+
+ if (unlikely (list_empty (qtd_list)))
+ qtd = 0;
+ else
+ qtd = list_entry (qtd_list->next, struct ehci_qtd,
+ qtd_list);
/* maybe patch the qh used for set_address */
if (unlikely (epnum == 0
&& le32_to_cpu (qh->hw_info1 & 0x7f) == 0))
qh->hw_info1 |= cpu_to_le32 (usb_pipedevice(urb->pipe));
- /* is an URB is queued to this qh already? */
- if (unlikely (!list_empty (&qh->qtd_list))) {
+ /* append to tds already queued to this qh? */
+ if (unlikely (!list_empty (&qh->qtd_list) && qtd)) {
struct ehci_qtd *last_qtd;
int short_rx = 0;
+ u32 hw_next;
/* update the last qtd's "next" pointer */
// dbg_qh ("non-empty qh", ehci, qh);
last_qtd = list_entry (qh->qtd_list.prev,
struct ehci_qtd, qtd_list);
+ hw_next = QTD_NEXT (qtd->qtd_dma);
last_qtd->hw_next = hw_next;
/* previous urb allows short rx? maybe optimize. */
@@ -803,6 +829,7 @@ submit_async (
* Interrupt code must cope with case of HC having it
* cached, and clobbering these updates.
* ... complicates getting rid of extra interrupts!
+ * (Or: use dummy td, so cache always stays valid.)
*/
if (qh->hw_current == cpu_to_le32 (last_qtd->qtd_dma)) {
wmb ();
@@ -822,31 +849,61 @@ submit_async (
*/
/* usb_clear_halt() means qh data toggle gets reset */
- if (usb_pipebulk (urb->pipe)
- && unlikely (!usb_gettoggle (urb->dev,
+ if (unlikely (!usb_gettoggle (urb->dev,
(epnum & 0x0f),
!(epnum & 0x10)))) {
clear_toggle (urb->dev,
epnum & 0x0f, !(epnum & 0x10), qh);
}
- qh_update (qh, qtd);
+ if (qtd)
+ qh_update (qh, qtd);
}
list_splice (qtd_list, qh->qtd_list.prev);
} else {
/* can't sleep here, we have ehci->lock... */
qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
- if (likely (qh != 0)) {
- // dbg_qh ("new qh", ehci, qh);
- dev->ep [epnum] = qh;
- }
+ // if (qh) dbg_qh ("new qh", ehci, qh);
+ *ptr = qh;
}
+ if (qh)
+ urb->hcpriv = qh_get (qh);
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+submit_async (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int mem_flags
+) {
+ struct ehci_qtd *qtd;
+ struct hcd_dev *dev;
+ int epnum;
+ unsigned long flags;
+ struct ehci_qh *qh = 0;
+
+ qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
+ dev = (struct hcd_dev *)urb->dev->hcpriv;
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein (urb->pipe))
+ epnum |= 0x10;
+
+ vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
+ ehci->hcd.self.bus_name, urb, urb->transfer_buffer_length,
+ epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
+ qtd, dev ? dev->ep [epnum] : (void *)~0);
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
/* Control/bulk operations through TTs don't need scheduling,
* the HC and TT handle it when the TT has a buffer ready.
*/
if (likely (qh != 0)) {
- urb->hcpriv = qh_get (qh);
if (likely (qh->qh_state == QH_STATE_IDLE))
qh_link_async (ehci, qh_get (qh));
}
@@ -873,7 +930,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
ehci->reclaim = 0;
ehci->reclaim_ready = 0;
- qh_completions (ehci, qh, 1);
+ qh_completions (ehci, qh);
// unlink any urb should now unlink all following urbs, so that
// relinking only happens for urbs before the unlinked ones.
@@ -973,13 +1030,15 @@ rescan:
spin_unlock_irqrestore (&ehci->lock, flags);
/* concurrent unlink could happen here */
- qh_completions (ehci, qh, 1);
+ qh_completions (ehci, qh);
spin_lock_irqsave (&ehci->lock, flags);
qh_put (ehci, qh);
}
- /* unlink idle entries (reduces PCI usage) */
+ /* unlink idle entries, reducing HC PCI usage as
+ * well as HCD schedule-scanning costs
+ */
if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
if (qh->qh_next.qh != qh) {
// dbg ("irq/empty");
@@ -987,6 +1046,7 @@ rescan:
} else {
// FIXME: arrange to stop
// after it's been idle a while.
+ // stop/restart isn't free...
}
}
qh = qh->qh_next.qh;
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 5e08204d34d9..1cb1b8a701bd 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -220,31 +220,31 @@ static int disable_periodic (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
+// FIXME microframe periods not yet handled
+
static void intr_deschedule (
struct ehci_hcd *ehci,
- unsigned frame,
struct ehci_qh *qh,
- unsigned period
+ int wait
) {
unsigned long flags;
int status;
-
- period >>= 3; // FIXME microframe periods not handled yet
+ unsigned frame = qh->start;
spin_lock_irqsave (&ehci->lock, flags);
do {
periodic_unlink (ehci, frame, qh);
qh_put (ehci, qh);
- frame += period;
+ frame += qh->period;
} while (frame < ehci->periodic_size);
qh->qh_state = QH_STATE_UNLINK;
qh->qh_next.ptr = 0;
- ehci->periodic_urbs--;
+ ehci->periodic_sched--;
/* maybe turn off periodic schedule */
- if (!ehci->periodic_urbs)
+ if (!ehci->periodic_sched)
status = disable_periodic (ehci);
else {
status = 0;
@@ -258,21 +258,35 @@ static void intr_deschedule (
* (yeech!) to be sure it's done.
* No other threads may be mucking with this qh.
*/
- if (!status && ((ehci_get_frame (&ehci->hcd) - frame) % period) == 0)
- udelay (125);
+ if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
+ if (wait) {
+ udelay (125);
+ qh->hw_next = EHCI_LIST_END;
+ } else {
+ /* we may not be IDLE yet, but if the qh is empty
+ * the race is very short. then if qh also isn't
+ * rescheduled soon, it won't matter. otherwise...
+ */
+ vdbg ("intr_deschedule...");
+ }
+ } else
+ qh->hw_next = EHCI_LIST_END;
qh->qh_state = QH_STATE_IDLE;
- qh->hw_next = EHCI_LIST_END;
+
+ /* update per-qh bandwidth utilization (for usbfs) */
+ ehci->hcd.self.bandwidth_allocated -=
+ (qh->usecs + qh->c_usecs) / qh->period;
vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d",
- qh, period, frame,
- atomic_read (&qh->refcount), ehci->periodic_urbs);
+ qh, qh->period, frame,
+ atomic_read (&qh->refcount), ehci->periodic_sched);
}
static int check_period (
struct ehci_hcd *ehci,
unsigned frame,
- int uframe,
+ unsigned uframe,
unsigned period,
unsigned usecs
) {
@@ -309,19 +323,142 @@ static int check_period (
return 1;
}
+static int check_intr_schedule (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ unsigned uframe,
+ const struct ehci_qh *qh,
+ u32 *c_maskp
+)
+{
+ int retval = -ENOSPC;
+
+ if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
+ goto done;
+ if (!qh->c_usecs) {
+ retval = 0;
+ *c_maskp = cpu_to_le32 (0);
+ goto done;
+ }
+
+ /* This is a split transaction; check the bandwidth available for
+ * the completion too. Check both worst and best case gaps: worst
+ * case is SPLIT near uframe end, and CSPLIT near start ... best is
+ * vice versa. Difference can be almost two uframe times, but we
+ * reserve unnecessary bandwidth (waste it) this way. (Actually
+ * even better cases exist, like immediate device NAK.)
+ *
+ * FIXME don't even bother unless we know this TT is idle in that
+ * range of uframes ... for now, check_period() allows only one
+ * interrupt transfer per frame, so needn't check "TT busy" status
+ * when scheduling a split (QH, SITD, or FSTN).
+ *
+ * FIXME ehci 0.96 and above can use FSTNs
+ */
+ if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
+ qh->period, qh->c_usecs))
+ goto done;
+ if (!check_period (ehci, frame, uframe + qh->gap_uf,
+ qh->period, qh->c_usecs))
+ goto done;
+
+ *c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf));
+ retval = 0;
+done:
+ return retval;
+}
+
+static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ int status;
+ unsigned uframe;
+ u32 c_mask;
+ unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
+
+ qh->hw_next = EHCI_LIST_END;
+ frame = qh->start;
+
+ /* reuse the previous schedule slots, if we can */
+ if (frame < qh->period) {
+ uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff);
+ status = check_intr_schedule (ehci, frame, --uframe,
+ qh, &c_mask);
+ } else {
+ uframe = 0;
+ c_mask = 0;
+ status = -ENOSPC;
+ }
+
+ /* else scan the schedule to find a group of slots such that all
+ * uframes have enough periodic bandwidth available.
+ */
+ if (status) {
+ frame = qh->period - 1;
+ do {
+ for (uframe = 0; uframe < 8; uframe++) {
+ status = check_intr_schedule (ehci,
+ frame, uframe, qh,
+ &c_mask);
+ if (status == 0)
+ break;
+ }
+ } while (status && --frame);
+ if (status)
+ goto done;
+ qh->start = frame;
+
+ /* reset S-frame and (maybe) C-frame masks */
+ qh->hw_info2 &= ~0xffff;
+ qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
+ } else
+ dbg ("reused previous qh %p schedule", qh);
+
+ /* stuff into the periodic schedule */
+ qh->qh_state = QH_STATE_LINKED;
+ dbg ("qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)",
+ qh, qh->usecs, qh->c_usecs,
+ qh->period, frame, uframe, qh->gap_uf);
+ do {
+ if (unlikely (ehci->pshadow [frame].ptr != 0)) {
+
+// FIXME -- just link toward the end, before any qh with a shorter period,
+// AND accomodate it already having been linked here (after some other qh)
+// AS WELL AS updating the schedule checking logic
+
+ BUG ();
+ } else {
+ ehci->pshadow [frame].qh = qh_get (qh);
+ ehci->periodic [frame] =
+ QH_NEXT (qh->qh_dma);
+ }
+ wmb ();
+ frame += qh->period;
+ } while (frame < ehci->periodic_size);
+
+ /* update per-qh bandwidth for usbfs */
+ ehci->hcd.self.bandwidth_allocated +=
+ (qh->usecs + qh->c_usecs) / qh->period;
+
+ /* maybe enable periodic schedule processing */
+ if (!ehci->periodic_sched++)
+ status = enable_periodic (ehci);
+done:
+ return status;
+}
+
static int intr_submit (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list,
int mem_flags
) {
- unsigned epnum, period;
- unsigned short usecs, c_usecs, gap_uf;
+ unsigned epnum;
unsigned long flags;
struct ehci_qh *qh;
struct hcd_dev *dev;
int is_input;
int status = 0;
+ struct list_head empty;
/* get endpoint and transfer/schedule data */
epnum = usb_pipeendpoint (urb->pipe);
@@ -329,198 +466,30 @@ static int intr_submit (
if (is_input)
epnum |= 0x10;
- /*
- * HS interrupt transfers are simple -- only one microframe. FS/LS
- * interrupt transfers involve a SPLIT in one microframe and CSPLIT
- * sometime later. We need to know how much time each will be
- * needed in each microframe and, for FS/LS, how many microframes
- * separate the two in the best case.
- */
- usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
- urb->transfer_buffer_length);
- if (urb->dev->speed == USB_SPEED_HIGH) {
- gap_uf = 0;
- c_usecs = 0;
-
- /* FIXME handle HS periods of less than 1 frame. */
- period = urb->interval >> 3;
- if (period < 1) {
- dbg ("intr period %d uframes, NYET!", urb->interval);
- status = -EINVAL;
- goto done;
- }
- } else {
- /* gap is a function of full/low speed transfer times */
- gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, is_input, 0,
- urb->transfer_buffer_length) / (125 * 1000);
-
- /* FIXME this just approximates SPLIT/CSPLIT times */
- if (is_input) { // SPLIT, gap, CSPLIT+DATA
- c_usecs = usecs + HS_USECS (0);
- usecs = HS_USECS (1);
- } else { // SPLIT+DATA, gap, CSPLIT
- usecs = usecs + HS_USECS (1);
- c_usecs = HS_USECS (0);
- }
-
- period = urb->interval;
- }
+ spin_lock_irqsave (&ehci->lock, flags);
+ dev = (struct hcd_dev *)urb->dev->hcpriv;
- /*
- * NOTE: current completion/restart logic doesn't handle more than
- * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this.
- * such big requests need many periods to transfer.
- *
- * FIXME want to change hcd core submit model to expect queuing
- * for all transfer types ... not just ISO and (with flag) BULK.
- * that means: getting rid of this check; handling the "interrupt
- * urb already queued" case below like bulk queuing is handled (no
- * errors possible!); and completly getting rid of that annoying
- * qh restart logic. simpler/smaller overall, and more flexible.
- */
- if (unlikely (qtd_list->next != qtd_list->prev)) {
- dbg ("only one intr qtd per urb allowed");
- status = -EINVAL;
+ /* get qh and force any scheduling errors */
+ INIT_LIST_HEAD (&empty);
+ qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]);
+ if (qh == 0) {
+ status = -ENOMEM;
goto done;
}
-
- spin_lock_irqsave (&ehci->lock, flags);
-
- /* get the qh (must be empty and idle) */
- dev = (struct hcd_dev *)urb->dev->hcpriv;
- qh = (struct ehci_qh *) dev->ep [epnum];
- if (qh) {
- /* only allow one queued interrupt urb per EP */
- if (unlikely (qh->qh_state != QH_STATE_IDLE
- || !list_empty (&qh->qtd_list))) {
- dbg ("interrupt urb already queued");
- status = -EBUSY;
- } else {
- /* maybe reset hardware's data toggle in the qh */
- if (unlikely (!usb_gettoggle (urb->dev, epnum & 0x0f,
- !(epnum & 0x10)))) {
- qh->hw_token |=
- __constant_cpu_to_le32 (QTD_TOGGLE);
- usb_settoggle (urb->dev, epnum & 0x0f,
- !(epnum & 0x10), 1);
- }
- /* trust the QH was set up as interrupt ... */
- list_splice (qtd_list, &qh->qtd_list);
- qh_update (qh, list_entry (qtd_list->next,
- struct ehci_qtd, qtd_list));
- qtd_list = &qh->qtd_list;
- }
- } else {
- /* can't sleep here, we have ehci->lock... */
- qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
- if (likely (qh != 0)) {
- // dbg ("new INTR qh %p", qh);
- dev->ep [epnum] = qh;
- qtd_list = &qh->qtd_list;
- } else
- status = -ENOMEM;
+ if (qh->qh_state == QH_STATE_IDLE) {
+ if ((status = qh_schedule (ehci, qh)) != 0)
+ goto done;
}
- /* Schedule this periodic QH. */
- if (likely (status == 0)) {
- unsigned frame = period;
-
- qh->hw_next = EHCI_LIST_END;
- qh->usecs = usecs;
- qh->c_usecs = c_usecs;
-
- urb->hcpriv = qh_get (qh);
- status = -ENOSPC;
-
- /* pick a set of schedule slots, link the QH into them */
- do {
- int uframe;
- u32 c_mask = 0;
-
- /* pick a set of slots such that all uframes have
- * enough periodic bandwidth available.
- */
- frame--;
- for (uframe = 0; uframe < 8; uframe++) {
- if (check_period (ehci, frame, uframe,
- period, usecs) == 0)
- continue;
-
- /* If this is a split transaction, check the
- * bandwidth available for the completion
- * too. check both best and worst case gaps:
- * worst case is SPLIT near uframe end, and
- * CSPLIT near start ... best is vice versa.
- * Difference can be almost two uframe times.
- *
- * FIXME don't even bother unless we know
- * this TT is idle in that uframe ... right
- * now we know only one interrupt transfer
- * will be scheduled per frame, so we don't
- * need to update/check TT state when we
- * schedule a split (QH, SITD, or FSTN).
- *
- * FIXME ehci 0.96 and above can use FSTNs
- */
- if (!c_usecs)
- break;
- if (check_period (ehci, frame,
- uframe + gap_uf,
- period, c_usecs) == 0)
- continue;
- if (check_period (ehci, frame,
- uframe + gap_uf + 1,
- period, c_usecs) == 0)
- continue;
-
- c_mask = 0x03 << (8 + uframe + gap_uf);
- c_mask = cpu_to_le32 (c_mask);
- break;
- }
- if (uframe == 8)
- continue;
-
- /* QH will run once each period, starting there */
- urb->start_frame = frame;
- status = 0;
-
- /* reset S-frame and (maybe) C-frame masks */
- qh->hw_info2 &= ~0xffff;
- qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
- // dbg_qh ("Schedule INTR qh", ehci, qh);
-
- /* stuff into the periodic schedule */
- qh->qh_state = QH_STATE_LINKED;
- vdbg ("qh %p usecs %d period %d.0 starting %d.%d",
- qh, qh->usecs, period, frame, uframe);
- do {
- if (unlikely (ehci->pshadow [frame].ptr != 0)) {
-// FIXME -- just link toward the end, before any qh with a shorter period,
-// AND handle it already being (implicitly) linked into this frame
-// AS WELL AS updating the check_period() logic
- BUG ();
- } else {
- ehci->pshadow [frame].qh = qh_get (qh);
- ehci->periodic [frame] =
- QH_NEXT (qh->qh_dma);
- }
- wmb ();
- frame += period;
- } while (frame < ehci->periodic_size);
-
- /* update bandwidth utilization records (for usbfs) */
- usb_claim_bandwidth (urb->dev, urb,
- (usecs + c_usecs) / period, 0);
+ /* then queue the urb's tds to the qh */
+ qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
+ BUG_ON (qh == 0);
- /* maybe enable periodic schedule processing */
- if (!ehci->periodic_urbs++)
- status = enable_periodic (ehci);
- break;
+ /* ... update usbfs periodic stats */
+ ehci->hcd.self.bandwidth_int_reqs++;
- } while (frame);
- }
- spin_unlock_irqrestore (&ehci->lock, flags);
done:
+ spin_unlock_irqrestore (&ehci->lock, flags);
if (status)
qtd_list_free (ehci, urb, qtd_list);
@@ -534,10 +503,6 @@ intr_complete (
struct ehci_qh *qh,
unsigned long flags /* caller owns ehci->lock ... */
) {
- struct ehci_qtd *qtd;
- struct urb *urb;
- int unlinking;
-
/* nothing to report? */
if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
!= 0))
@@ -547,43 +512,14 @@ intr_complete (
return flags;
}
- qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list);
- urb = qtd->urb;
- unlinking = (urb->status == -ENOENT) || (urb->status == -ECONNRESET);
-
- /* call any completions, after patching for reactivation */
+ /* handle any completions */
spin_unlock_irqrestore (&ehci->lock, flags);
- /* NOTE: currently restricted to one qtd per qh! */
- if (qh_completions (ehci, qh, 0) == 0)
- urb = 0;
+ qh_completions (ehci, qh);
spin_lock_irqsave (&ehci->lock, flags);
- /* never reactivate requests that were unlinked ... */
- if (likely (urb != 0)) {
- if (unlinking
- || urb->status == -ECONNRESET
- || urb->status == -ENOENT
- // || (urb->dev == null)
- || ehci->hcd.state == USB_STATE_HALT)
- urb = 0;
- // FIXME look at all those unlink cases ... we always
- // need exactly one completion that reports unlink.
- // the one above might not have been it!
- }
+ if (unlikely (list_empty (&qh->qtd_list)))
+ intr_deschedule (ehci, qh, 0);
- /* normally reactivate */
- if (likely (urb != 0)) {
- if (usb_pipeout (urb->pipe))
- pci_dma_sync_single (ehci->hcd.pdev,
- qtd->buf_dma,
- urb->transfer_buffer_length,
- PCI_DMA_TODEVICE);
- urb->status = -EINPROGRESS;
- urb->actual_length = 0;
-
- /* patch qh and restart */
- qh_update (qh, qtd);
- }
return flags;
}
@@ -806,7 +742,7 @@ static int get_iso_range (
/* calculate the legal range [start,max) */
now = readl (&ehci->regs->frame_index) + 1; /* next uframe */
- if (!ehci->periodic_urbs)
+ if (!ehci->periodic_sched)
now += 8; /* startup delay */
now %= mod;
end = now + mod;
@@ -926,7 +862,7 @@ itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
usb_claim_bandwidth (urb->dev, urb, usecs, 1);
/* maybe enable periodic schedule processing */
- if (!ehci->periodic_urbs++) {
+ if (!ehci->periodic_sched++) {
if ((status = enable_periodic (ehci)) != 0) {
// FIXME deschedule right away
err ("itd_schedule, enable = %d", status);
@@ -1009,8 +945,8 @@ itd_complete (
spin_lock_irqsave (&ehci->lock, flags);
/* defer stopping schedule; completion can submit */
- ehci->periodic_urbs--;
- if (!ehci->periodic_urbs)
+ ehci->periodic_sched--;
+ if (!ehci->periodic_sched)
(void) disable_periodic (ehci);
return flags;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index c2cea19d2a56..7efa35064789 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -50,7 +50,7 @@ struct ehci_hcd { /* one per controller */
union ehci_shadow *pshadow; /* mirror hw periodic table */
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 */
struct tasklet_struct tasklet;
@@ -72,7 +72,7 @@ struct ehci_hcd { /* one per controller */
};
/* 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 !!! */
#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */
@@ -287,12 +287,20 @@ struct ehci_qh {
struct list_head qtd_list; /* sw qtd list */
atomic_t refcount;
- unsigned short usecs; /* intr bandwidth */
- unsigned short c_usecs; /* ... split completion bw */
- short qh_state;
+
+ u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still 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)));
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 7518b8992b8f..eeb11c335f0d 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -938,7 +938,7 @@ rescan_this:
/* ED's now officially unlinked, hc doesn't see */
ed->state = ED_IDLE;
ed->hwINFO &= ~ED_SKIP;
- ed->hwHeadP &= ~cpu_to_le32 (ED_H);
+ ed->hwHeadP &= ~ED_H;
ed->hwNextED = 0;
/* but if there's work queued, reschedule */
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 21fb4e6c1835..b223f089723e 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -147,7 +147,8 @@ static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
srb->host_scribble = (unsigned char *)us;
/* 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;
us->srb = srb;
diff --git a/include/linux/usb.h b/include/linux/usb.h
index d9907d770a30..7f44f0ff05ef 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -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 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_NO_FSBR 0x0020 /* UHCI-specific */
#define USB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet */
@@ -771,11 +772,15 @@ typedef void (*usb_complete_t)(struct urb *);
* @transfer_flags: A variety of flags may be used to affect how URB
* submission, unlinking, or operation are handled. Different
* kinds of URB can use different flags.
- * @transfer_buffer: For non-iso transfers, this identifies the buffer
- * to (or from) which the I/O request will be performed. This
- * buffer must be suitable for DMA; allocate it with kmalloc()
+ * @transfer_buffer: This identifies the buffer to (or from) which
+ * the I/O request will be performed (unless URB_NO_DMA_MAP is set).
+ * This buffer must be suitable for DMA; allocate it with kmalloc()
* 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
* be broken up into chunks according to the current maximum packet
* size for the endpoint, which is a function of the configuration
@@ -789,6 +794,11 @@ typedef void (*usb_complete_t)(struct urb *);
* @setup_packet: Only used for control transfers, this points to eight bytes
* of setup data. Control transfers always start by sending this data
* 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
* transfers.
* @number_of_packets: Lists the number of ISO transfer buffers.
@@ -811,6 +821,23 @@ typedef void (*usb_complete_t)(struct urb *);
* are submitted using usb_submit_urb(), and pending requests may be canceled
* 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:
*
* All URBs submitted must initialize dev, pipe,
@@ -818,10 +845,10 @@ typedef void (*usb_complete_t)(struct urb *);
* The USB_ASYNC_UNLINK transfer flag affects later invocations of
* 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
* 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
* use the USB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers
@@ -896,10 +923,12 @@ struct urb
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
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 actual_length; /* (return) actual transfer length */
int bandwidth; /* bandwidth for INT/ISO request */
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 number_of_packets; /* (in) number of ISO packets */
int interval; /* (in) transfer interval (INT/ISO) */
@@ -910,6 +939,8 @@ struct urb
struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */
};
+/* -------------------------------------------------------------------------- */
+
/**
* usb_fill_control_urb - initializes a control urb
* @urb: pointer to the urb to initialize.
@@ -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_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 *
*-------------------------------------------------------------------*/