summaryrefslogtreecommitdiffstats
path: root/drivers/usb/uhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/uhci.c')
-rw-r--r--drivers/usb/uhci.c948
1 files changed, 467 insertions, 481 deletions
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
index ec4cad491..c3b4ebc2e 100644
--- a/drivers/usb/uhci.c
+++ b/drivers/usb/uhci.c
@@ -21,7 +21,6 @@
* - working around the horridness of the rest
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -34,15 +33,14 @@
#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
+#define DEBUG
+#include <linux/usb.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
-#define DEBUG
-#include "usb.h"
-
#include "uhci.h"
#include "uhci-debug.h"
@@ -54,18 +52,21 @@ MODULE_PARM(debug, "i");
static kmem_cache_t *uhci_td_cachep;
static kmem_cache_t *uhci_qh_cachep;
+static kmem_cache_t *uhci_up_cachep; /* urb_priv */
static LIST_HEAD(uhci_list);
static int rh_submit_urb(urb_t *urb);
static int rh_unlink_urb(urb_t *urb);
static int uhci_get_current_frame_number(struct usb_device *dev);
-static void uhci_stop_hc_schedule(struct uhci *uhci);
-static void uhci_start_hc_schedule(struct uhci *uhci);
+static int uhci_unlink_generic(urb_t *urb);
static int uhci_unlink_urb(urb_t *urb);
#define min(a,b) (((a)<(b))?(a):(b))
+/* If a transfer is still active after this much time, turn off FSBR */
+#define IDLE_TIMEOUT (HZ / 20) /* 50 ms */
+
/*
* Only the USB core should call uhci_alloc_dev and uhci_free_dev
*/
@@ -78,7 +79,7 @@ static int uhci_free_dev(struct usb_device *dev)
{
urb_t *u;
struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
- struct list_head *tmp, *head = &uhci->urb_list;
+ struct list_head *tmp, *next, *head = &uhci->urb_list;
unsigned long flags;
/* Walk through the entire URB list and forcefully remove any */
@@ -88,38 +89,18 @@ static int uhci_free_dev(struct usb_device *dev)
while (tmp != head) {
u = list_entry(tmp, urb_t, urb_list);
+ next = tmp->next;
+
if (u->dev == dev)
uhci_unlink_urb(u);
+
+ tmp = next;
}
nested_unlock(&uhci->urblist_lock, flags);
return 0;
}
-/*
- * UHCI interrupt list operations..
- */
-static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td)
-{
- unsigned long flags;
-
- nested_lock(&uhci->irqlist_lock, flags);
- list_add(&td->irq_list, &uhci->interrupt_list);
- nested_unlock(&uhci->irqlist_lock, flags);
-}
-
-static void uhci_remove_irq_list(struct uhci *uhci, struct uhci_td *td)
-{
- unsigned long flags;
-
- nested_lock(&uhci->irqlist_lock, flags);
- if (td->irq_list.next != &td->irq_list) {
- list_del(&td->irq_list);
- INIT_LIST_HEAD(&td->irq_list);
- }
- nested_unlock(&uhci->irqlist_lock, flags);
-}
-
static void uhci_add_urb_list(struct uhci *uhci, struct urb *urb)
{
unsigned long flags;
@@ -141,6 +122,54 @@ static void uhci_remove_urb_list(struct uhci *uhci, struct urb *urb)
nested_unlock(&uhci->urblist_lock, flags);
}
+static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
+{
+ struct uhci_td *td;
+
+ td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
+ if (!td)
+ return NULL;
+
+ td->link = UHCI_PTR_TERM;
+ td->buffer = 0;
+
+ td->frameptr = NULL;
+ td->nexttd = td->prevtd = NULL;
+ td->list.next = td->list.prev = NULL;
+ td->dev = dev;
+
+ usb_inc_dev_use(dev);
+
+ return td;
+}
+
+static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
+ __u32 info, __u32 buffer)
+{
+ td->status = status;
+ td->info = info;
+ td->buffer = buffer;
+}
+
+static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->framelist_lock, flags);
+
+ /* Fix the linked list pointers */
+ td->nexttd = skeltd->nexttd;
+ td->prevtd = skeltd;
+ if (skeltd->nexttd)
+ skeltd->nexttd->prevtd = td;
+ skeltd->nexttd = td;
+
+ td->link = skeltd->link;
+ skeltd->link = virt_to_bus(td);
+
+ spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+}
+
/*
* We insert Isochronous transfers directly into the frame list at the
* beginning
@@ -157,6 +186,7 @@ static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, uns
framenum %= UHCI_NUMFRAMES;
spin_lock_irqsave(&uhci->framelist_lock, flags);
+
td->frameptr = &uhci->fl->frame[framenum];
td->link = uhci->fl->frame[framenum];
if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) {
@@ -166,6 +196,7 @@ static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, uns
nexttd->frameptr = NULL;
}
uhci->fl->frame[framenum] = virt_to_bus(td);
+
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
}
@@ -173,6 +204,10 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
{
unsigned long flags;
+ /* If it's not inserted, don't remove it */
+ if (!td->frameptr && !td->prevtd && !td->nexttd)
+ return;
+
spin_lock_irqsave(&uhci->framelist_lock, flags);
if (td->frameptr) {
*(td->frameptr) = td->link;
@@ -195,77 +230,41 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
}
-static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&uhci->framelist_lock, flags);
-
- /* Fix the linked list pointers */
- td->nexttd = skeltd->nexttd;
- td->prevtd = skeltd;
- if (skeltd->nexttd)
- skeltd->nexttd->prevtd = td;
- skeltd->nexttd = td;
-
- td->link = skeltd->link;
- skeltd->link = virt_to_bus(td);
-
- spin_unlock_irqrestore(&uhci->framelist_lock, flags);
-}
-
/*
* Inserts a td into qh list at the top.
*/
-static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *begin)
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
{
- struct uhci_td *td, *prevtd;
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci_td *td, *prevtd = NULL;
- if (!begin) /* Nothing to do */
+ if (!urbp)
return;
- /* Grab the first TD and add it to the QH */
- td = begin;
- qh->element = virt_to_bus(td) | UHCI_PTR_DEPTH;
+ td = urbp->list.begin;
+ if (!td)
+ return;
+
+ /* Add the first TD to the QH element pointer */
+ qh->element = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
- /* Go through the rest of the TD's, link them together */
prevtd = td;
- td = td->next;
- while (td) {
- prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH;
+
+ /* Then link the rest of the TD's */
+ for (td = td->list.next; td; td = td->list.next) {
+ prevtd->link = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
prevtd = td;
- td = td->next;
}
prevtd->link = UHCI_PTR_TERM;
}
-static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
-{
- struct uhci_td *td;
-
- td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
- if (!td)
- return NULL;
-
- td->link = UHCI_PTR_TERM;
- td->buffer = 0;
-
- td->frameptr = NULL;
- td->nexttd = td->prevtd = NULL;
- td->next = NULL;
- td->dev = dev;
- INIT_LIST_HEAD(&td->irq_list);
- INIT_LIST_HEAD(&td->list);
-
- usb_inc_dev_use(dev);
-
- return td;
-}
-
static void uhci_free_td(struct uhci_td *td)
{
+ if (td->list.next || td->list.prev)
+ dbg("td is still in URB list!");
+
kmem_cache_free(uhci_td_cachep, td);
if (td->dev)
@@ -286,7 +285,7 @@ static struct uhci_qh *uhci_alloc_qh(struct usb_device *dev)
qh->dev = dev;
qh->prevqh = qh->nextqh = NULL;
- INIT_LIST_HEAD(&qh->list);
+ INIT_LIST_HEAD(&qh->remove_list);
usb_inc_dev_use(dev);
@@ -333,14 +332,31 @@ static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
qh->nextqh->prevqh = qh->prevqh;
qh->prevqh = qh->nextqh = NULL;
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+
+ spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+ list_add(&qh->remove_list, &uhci->qh_remove_list);
+ spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
}
-static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
- __u32 info, __u32 buffer)
+struct urb_priv *uhci_alloc_urb_priv(struct urb *urb)
{
- td->status = status;
- td->info = info;
- td->buffer = buffer;
+ struct urb_priv *urbp;
+
+ urbp = kmem_cache_alloc(uhci_up_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL);
+ if (!urbp)
+ return NULL;
+
+ memset((void *)urbp, 0, sizeof(*urbp));
+
+ urbp->list.begin = urbp->list.end = NULL;
+
+ urb->hcpriv = urbp;
+
+ urbp->inserttime = jiffies;
+
+ usb_inc_dev_use(urb->dev);
+
+ return urbp;
}
static void uhci_add_td_to_urb(urb_t *urb, struct uhci_td *td)
@@ -349,35 +365,110 @@ static void uhci_add_td_to_urb(urb_t *urb, struct uhci_td *td)
td->urb = urb;
- if (urbp->end)
- urbp->end->next = td;
+ if (!urbp->list.begin)
+ urbp->list.begin = td;
+
+ if (urbp->list.end) {
+ urbp->list.end->list.next = td;
+ td->list.prev = urbp->list.end;
+ }
+ urbp->list.end = td;
+}
+
+static void uhci_remove_td_from_urb(urb_t *urb, struct uhci_td *td)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+ if (!urbp->list.begin && !urbp->list.end)
+ return;
+
+ if (td->list.prev)
+ td->list.prev->list.next = td->list.next;
+ else
+ urbp->list.begin = td->list.next;
+
+ if (td->list.next)
+ td->list.next->list.prev = td->list.prev;
+ else
+ urbp->list.end = td->list.prev;
+
+ td->list.next = td->list.prev = NULL;
+ td->urb = NULL;
+}
+
+static void uhci_destroy_urb_priv(urb_t *urb)
+{
+ struct urb_priv *urbp;
+ struct uhci *uhci;
+ struct uhci_td *td, *nexttd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&urb->lock, flags);
+
+ urbp = (struct urb_priv *)urb->hcpriv;
+ if (!urbp)
+ return;
+
+ if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+ return;
+
+ uhci = urb->dev->bus->hcpriv;
+
+ td = urbp->list.begin;
+ while (td) {
+ nexttd = td->list.next;
+
+ uhci_remove_td_from_urb(urb, td);
+
+ uhci_remove_td(uhci, td);
- urbp->end = td;
+ uhci_free_td(td);
+
+ td = nexttd;
+ }
+
+ urb->hcpriv = NULL;
+ kmem_cache_free(uhci_up_cachep, urbp);
- if (!urbp->begin)
- urbp->begin = td;
+ spin_unlock_irqrestore(&urb->lock, flags);
+
+ usb_dec_dev_use(urb->dev);
}
-void uhci_inc_fsbr(struct uhci *uhci)
+static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
{
unsigned long flags;
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+ if (!urbp)
+ return;
spin_lock_irqsave(&uhci->framelist_lock, flags);
- if (!uhci->fsbr++)
- uhci->skel_term_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+ if (!urbp->fsbr) {
+ urbp->fsbr = 1;
+ if (!uhci->fsbr++)
+ uhci->skel_term_td.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+ }
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
}
-void uhci_dec_fsbr(struct uhci *uhci)
+static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
{
unsigned long flags;
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+ if (!urbp)
+ return;
spin_lock_irqsave(&uhci->framelist_lock, flags);
- if (!--uhci->fsbr)
- uhci->skel_term_qh.link = UHCI_PTR_TERM;
+ if (urbp->fsbr) {
+ urbp->fsbr = 0;
+ if (!--uhci->fsbr)
+ uhci->skel_term_td.link = UHCI_PTR_TERM;
+ }
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
}
@@ -419,14 +510,14 @@ static int uhci_map_status(int status, int dir_out)
*/
static int uhci_submit_control(urb_t *urb)
{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct uhci_td *td;
struct uhci_qh *qh;
unsigned long destination, status;
- struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
int len = urb->transfer_buffer_length;
unsigned char *data = urb->transfer_buffer;
- struct urb_priv *urbp;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
@@ -434,14 +525,6 @@ static int uhci_submit_control(urb_t *urb)
/* 3 errors */
status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);
- urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
- if (!urbp)
- return -ENOMEM;
-
- urbp->begin = urbp->end = NULL;
-
- urb->hcpriv = urbp;
-
/*
* Build the TD for the control request
*/
@@ -465,18 +548,16 @@ static int uhci_submit_control(urb_t *urb)
/*
* Build the DATA TD's
*/
- td = uhci_alloc_td(urb->dev);
- if (!td) {
- /* FIXME: Free the TD's */
- return -ENOMEM;
- }
-
while (len > 0) {
int pktsze = len;
if (pktsze > maxsze)
pktsze = maxsze;
+ td = uhci_alloc_td(urb->dev);
+ if (!td)
+ return -ENOMEM;
+
/* Alternate Data0/1 (start with Data1) */
destination ^= 1 << TD_TOKEN_TOGGLE;
@@ -486,16 +567,16 @@ static int uhci_submit_control(urb_t *urb)
data += pktsze;
len -= pktsze;
-
- td = uhci_alloc_td(urb->dev);
- if (!td)
- /* FIXME: Free all of the previously allocated td's */
- return -ENOMEM;
}
/*
* Build the final TD for control status
- *
+ */
+ td = uhci_alloc_td(urb->dev);
+ if (!td)
+ return -ENOMEM;
+
+ /*
* It's IN if the pipe is an output pipe or we're not expecting
* data back.
*/
@@ -513,20 +594,19 @@ static int uhci_submit_control(urb_t *urb)
uhci_fill_td(td, status | TD_CTRL_IOC,
destination | (UHCI_NULL_DATA_SIZE << 21), 0);
- uhci_add_irq_list(uhci, td);
-
qh = uhci_alloc_qh(urb->dev);
- if (!qh) {
- /* FIXME: Free all of the TD's */
+ if (!qh)
return -ENOMEM;
- }
- uhci_insert_tds_in_qh(qh, urbp->begin);
- if (!(urb->pipe & TD_CTRL_LS)) {
- uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);
- uhci_inc_fsbr(uhci);
- } else
+ /* Low speed or small transfers gets a different queue and treatment */
+ if (urb->pipe & TD_CTRL_LS) {
+ uhci_insert_tds_in_qh(qh, urb, 0);
uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, qh);
+ } else {
+ uhci_insert_tds_in_qh(qh, urb, 1);
+ uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);
+ uhci_inc_fsbr(uhci, urb);
+ }
urbp->qh = qh;
@@ -537,66 +617,22 @@ static int uhci_submit_control(urb_t *urb)
return -EINPROGRESS;
}
-/* This is also the uhci_unlink_bulk function */
-static int uhci_unlink_control(urb_t *urb)
+static int uhci_result_control(urb_t *urb)
{
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
- struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
- int notfinished;
+ unsigned int status;
if (!urbp)
return -EINVAL;
- notfinished = (urb->status == -EINPROGRESS);
-
- if (notfinished)
- uhci_stop_hc_schedule(uhci);
-
- if (!(urb->pipe & TD_CTRL_LS))
- uhci_dec_fsbr(uhci);
-
- uhci_remove_qh(uhci, urbp->qh);
- uhci_free_qh(urbp->qh);
-
- /* Go through the rest of the TD's, deleting them, then scheduling */
- /* their deletion */
- td = urbp->begin;
- while (td) {
- struct uhci_td *next = td->next;
-
- if (td->status & TD_CTRL_IOC)
- uhci_remove_irq_list(uhci, td);
-
- uhci_free_td(td);
-
- td = next;
- }
-
- if (notfinished)
- uhci_start_hc_schedule(uhci);
-
- kfree(urbp);
- urb->hcpriv = NULL;
-
- uhci_remove_urb_list(uhci, urb);
-
- return 0;
-}
-
-static int uhci_result_control(urb_t *urb)
-{
- struct urb_priv *urbp = urb->hcpriv;
- struct uhci_td *td;
- unsigned int status;
-
- td = urbp->begin;
- if (!td) /* Nothing to do */
+ td = urbp->list.begin;
+ if (!td)
return -EINVAL;
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
- status = uhci_status_bits(td->status);
+ status = uhci_status_bits(td->status);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -605,9 +641,10 @@ static int uhci_result_control(urb_t *urb)
urb->actual_length = 0;
+ td = td->list.next;
+
/* The rest of the TD's (but the last) are data */
- td = td->next;
- while (td && td->next) {
+ while (td && td->list.next) {
status = uhci_status_bits(td->status);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -617,13 +654,14 @@ static int uhci_result_control(urb_t *urb)
/* If SPD is set then we received a short packet */
/* There will be no status phase at the end */
/* FIXME: Re-setup the queue to run the STATUS phase? */
- if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info)))
+ if ((td->status & TD_CTRL_SPD) &&
+ (uhci_actual_length(td->status) < uhci_expected_length(td->info)))
return 0;
if (status)
goto td_error;
- td = td->next;
+ td = td->list.next;
}
/* Control status phase */
@@ -648,18 +686,16 @@ static int uhci_result_control(urb_t *urb)
td_error:
/* Some debugging code */
if (debug) {
- dbg("uhci_result_control() failed with status %x",
- status);
+ dbg("uhci_result_control() failed with status %x", status);
/* Print the chain for debugging purposes */
uhci_show_queue(urbp->qh);
}
- if (status & TD_CTRL_STALLED) {
+ if (status & TD_CTRL_STALLED)
/* endpoint has stalled - mark it halted */
usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
uhci_packetout(td->info));
- }
return uhci_map_status(status, uhci_packetout(td->info));
}
@@ -672,7 +708,6 @@ static int uhci_submit_interrupt(urb_t *urb)
struct uhci_td *td;
unsigned long destination, status;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
- struct urb_priv *urbp;
if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
return -EINVAL;
@@ -680,16 +715,7 @@ static int uhci_submit_interrupt(urb_t *urb)
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
- status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD |
- TD_CTRL_IOC;
-
- urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
- if (!urbp)
- return -ENOMEM;
-
- urbp->begin = urbp->end = NULL;
-
- urb->hcpriv = urbp;
+ status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
td = uhci_alloc_td(urb->dev);
if (!td)
@@ -698,72 +724,70 @@ static int uhci_submit_interrupt(urb_t *urb)
destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
destination |= ((urb->transfer_buffer_length - 1) << 21);
+ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination,
virt_to_bus(urb->transfer_buffer));
- uhci_add_irq_list(uhci, td);
-
uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td);
uhci_add_urb_list(uhci, urb);
- usb_inc_dev_use(urb->dev);
-
return -EINPROGRESS;
}
-static int uhci_unlink_interrupt(urb_t *urb)
+static int uhci_result_interrupt(urb_t *urb)
{
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
- struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
- int notfinished;
+ unsigned int status;
if (!urbp)
return -EINVAL;
- notfinished = (urb->status == -EINPROGRESS);
+ urb->actual_length = 0;
- if (notfinished)
- uhci_stop_hc_schedule(uhci);
+ for (td = urbp->list.begin; td; td = td->list.next) {
+ status = uhci_status_bits(td->status);
+ if (status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
- td = urbp->begin;
- uhci_remove_td(uhci, td);
- if (td->status & TD_CTRL_IOC)
- uhci_remove_irq_list(uhci, td);
- uhci_free_td(td);
+ urb->actual_length += uhci_actual_length(td->status);
- if (notfinished)
- uhci_start_hc_schedule(uhci);
+ /* If SPD is set then we received a short packet */
+ if ((td->status & TD_CTRL_SPD) &&
+ (uhci_actual_length(td->status) < uhci_expected_length(td->info))) {
+ usb_settoggle(urb->dev, uhci_endpoint(td->info),
+ uhci_packetout(td->info),
+ uhci_toggle(td->info) ^ 1);
- kfree(urbp);
- urb->hcpriv = NULL;
+ return 0;
+ }
- uhci_remove_urb_list(uhci, urb);
+ if (status)
+ goto td_error;
+ }
return 0;
-}
-
-static int uhci_result_interrupt(urb_t *urb)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td;
- int status;
- if (!urbp)
- return -EINVAL;
-
- td = urbp->begin;
- if (!td)
- return -EINVAL;
+td_error:
+ /* Some debugging code */
+ if (debug) {
+ dbg("uhci_result_interrupt/bulk() failed with status %x",
+ status);
- status = uhci_status_bits(td->status);
- if (status & TD_CTRL_ACTIVE)
- return -EINPROGRESS;
+ /* Print the chain for debugging purposes */
+ if (urbp->qh)
+ uhci_show_queue(urbp->qh);
+ else
+ uhci_show_td(td);
+ }
- if (!status)
- urb->actual_length = uhci_actual_length(td->status);
+ if (status & TD_CTRL_STALLED)
+ /* endpoint has stalled - mark it halted */
+ usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+ uhci_packetout(td->info));
return uhci_map_status(status, uhci_packetout(td->info));
}
@@ -773,12 +797,17 @@ static void uhci_reset_interrupt(urb_t *urb)
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td;
- td = urbp->begin;
+ if (!urbp)
+ return;
+
+ td = urbp->list.begin;
+ if (!td)
+ return;
- usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
td->info &= ~(1 << TD_TOKEN_TOGGLE);
td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE);
+ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
urb->status = -EINPROGRESS;
}
@@ -795,27 +824,23 @@ static int uhci_submit_bulk(urb_t *urb)
int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
int len = urb->transfer_buffer_length;
unsigned char *data = urb->transfer_buffer;
- struct urb_priv *urbp;
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
if (len < 0)
return -EINVAL;
+ /* Can't have low speed bulk transfers */
+ if (urb->pipe & TD_CTRL_LS)
+ return -EINVAL;
+
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
/* 3 errors */
- status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);
+ status = TD_CTRL_ACTIVE | (3 << 27);
if (!(urb->transfer_flags & USB_DISABLE_SPD))
status |= TD_CTRL_SPD;
- urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
- if (!urbp)
- return -ENOMEM;
-
- urbp->begin = urbp->end = NULL;
-
- urb->hcpriv = urbp;
-
/*
* Build the DATA TD's
*/
@@ -826,10 +851,8 @@ static int uhci_submit_bulk(urb_t *urb)
pktsze = maxsze;
td = uhci_alloc_td(urb->dev);
- if (!td) {
- /* FIXME: Free the TD's */
+ if (!td)
return -ENOMEM;
- }
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) |
@@ -840,86 +863,31 @@ static int uhci_submit_bulk(urb_t *urb)
data += pktsze;
len -= maxsze;
- if (len <= 0) {
+ if (len <= 0)
td->status |= TD_CTRL_IOC;
- uhci_add_irq_list(uhci, td);
- }
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
}
qh = uhci_alloc_qh(urb->dev);
- if (!qh) {
- /* FIXME: Free all of the TD's */
+ if (!qh)
return -ENOMEM;
- }
- uhci_insert_tds_in_qh(qh, urbp->begin);
+
+ uhci_insert_tds_in_qh(qh, urb, 1);
uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
urbp->qh = qh;
uhci_add_urb_list(uhci, urb);
- usb_inc_dev_use(urb->dev);
-
- uhci_inc_fsbr(uhci);
+ uhci_inc_fsbr(uhci, urb);
return -EINPROGRESS;
}
-/* We can use the control unlink since they're identical */
-#define uhci_unlink_bulk uhci_unlink_control
-
-static int uhci_result_bulk(urb_t *urb)
-{
- struct urb_priv *urbp = urb->hcpriv;
- struct uhci_td *td;
- unsigned int status;
-
- urb->actual_length = 0;
-
- /* The rest of the TD's (but the last) are data */
- for (td = urbp->begin; td; td = td->next) {
- status = uhci_status_bits(td->status);
- if (status & TD_CTRL_ACTIVE)
- return -EINPROGRESS;
-
- urb->actual_length += uhci_actual_length(td->status);
-
- /* If SPD is set then we received a short packet */
- if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info))) {
- usb_settoggle(urb->dev, uhci_endpoint(td->info),
- uhci_packetout(td->info),
- uhci_toggle(td->info) ^ 1);
-
- return 0;
- }
-
- if (status)
- goto td_error;
- }
-
- return 0;
-
-td_error:
- /* Some debugging code */
- if (debug) {
- dbg("uhci_result_bulk() failed with status %x",
- status);
-
- /* Print the chain for debugging purposes */
- uhci_show_queue(urbp->qh);
- }
-
- if (status & TD_CTRL_STALLED) {
- /* endpoint has stalled - mark it halted */
- usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
- uhci_packetout(td->info));
- }
-
- return uhci_map_status(status, uhci_packetout(td->info));
-}
+/* We can use the result interrupt since they're identical */
+#define uhci_result_bulk uhci_result_interrupt
/*
* Isochronous transfers
@@ -929,6 +897,7 @@ static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int
urb_t *u, *last_urb = NULL;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct list_head *tmp, *head = &uhci->urb_list;
+ int ret = 0;
unsigned long flags;
nested_lock(&uhci->urblist_lock, flags);
@@ -945,14 +914,16 @@ static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int
}
tmp = tmp->next;
}
- nested_unlock(&uhci->urblist_lock, flags);
if (last_urb) {
*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
- return 0;
+ ret = 0;
} else
- return -1; // no previous urb found
+ ret = -1; /* no previous urb found */
+ nested_unlock(&uhci->urblist_lock, flags);
+
+ return ret;
}
static int isochronous_find_start(urb_t *urb)
@@ -985,7 +956,6 @@ static int uhci_submit_isochronous(urb_t *urb)
{
struct uhci_td *td;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
- struct urb_priv *urbp;
int i, ret, framenum;
int status, destination;
@@ -996,85 +966,30 @@ static int uhci_submit_isochronous(urb_t *urb)
if (ret)
return ret;
- urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
- if (!urbp)
- return -ENOMEM;
-
- urbp->begin = urbp->end = NULL;
-
- urb->hcpriv = urbp;
-
framenum = urb->start_frame;
for (i = 0; i < urb->number_of_packets; i++, framenum++) {
if (!urb->iso_frame_desc[i].length)
continue;
td = uhci_alloc_td(urb->dev);
- if (!td) {
- /* FIXME: Free the TD's */
+ if (!td)
return -ENOMEM;
- }
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21),
virt_to_bus(urb->transfer_buffer + urb->iso_frame_desc[i].offset));
- if (i + 1 >= urb->number_of_packets) {
+ if (i + 1 >= urb->number_of_packets)
td->status |= TD_CTRL_IOC;
- uhci_add_irq_list(uhci, td);
- }
uhci_insert_td_frame_list(uhci, td, framenum);
}
uhci_add_urb_list(uhci, urb);
- usb_inc_dev_use(urb->dev);
-
return -EINPROGRESS;
}
-static int uhci_unlink_isochronous(urb_t *urb)
-{
- struct urb_priv *urbp = urb->hcpriv;
- struct uhci_td *td;
- struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
- int notfinished;
-
- if (!urbp)
- return -EINVAL;
-
- notfinished = (urb->status == -EINPROGRESS);
-
- if (notfinished)
- uhci_stop_hc_schedule(uhci);
-
- /* Go through the rest of the TD's, deleting them, then scheduling */
- /* their deletion */
- td = urbp->begin;
- while (td) {
- struct uhci_td *next = td->next;
-
- uhci_remove_td(uhci, td);
-
- if (td->status & TD_CTRL_IOC)
- uhci_remove_irq_list(uhci, td);
- uhci_free_td(td);
-
- td = next;
- }
-
- if (notfinished)
- uhci_start_hc_schedule(uhci);
-
- kfree(urbp);
- urb->hcpriv = NULL;
-
- uhci_remove_urb_list(uhci, urb);
-
- return 0;
-}
-
static int uhci_result_isochronous(urb_t *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
@@ -1082,19 +997,17 @@ static int uhci_result_isochronous(urb_t *urb)
int status;
int i, ret = 0;
- td = urbp->end;
- if (!td) /* Nothing to do */
+ if (!urbp)
return -EINVAL;
- status = uhci_status_bits(td->status);
- if (status & TD_CTRL_ACTIVE)
- return -EINPROGRESS;
-
urb->actual_length = 0;
- for (i = 0, td = urbp->begin; td; i++, td = td->next) {
+ for (i = 0, td = urbp->list.begin; td; i++, td = td->list.next) {
int actlength;
+ if (td->status & TD_CTRL_ACTIVE)
+ return -EINPROGRESS;
+
actlength = uhci_actual_length(td->status);
urb->iso_frame_desc[i].actual_length = actlength;
urb->actual_length += actlength;
@@ -1114,17 +1027,26 @@ static int uhci_submit_urb(urb_t *urb)
{
int ret = -EINVAL;
struct uhci *uhci;
+ unsigned long flags;
if (!urb)
return -EINVAL;
- if (!urb->dev || !urb->dev->bus)
+ if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
return -ENODEV;
uhci = (struct uhci *)urb->dev->bus->hcpriv;
+ /* Short circuit the virtual root hub */
if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
- return rh_submit_urb(urb); /* Virtual root hub */
+ return rh_submit_urb(urb);
+
+ spin_lock_irqsave(&urb->lock, flags);
+
+ if (!uhci_alloc_urb_priv(urb)) {
+ spin_unlock_irqrestore(&urb->lock, flags);
+ return -ENOMEM;
+ }
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
@@ -1142,20 +1064,30 @@ static int uhci_submit_urb(urb_t *urb)
}
urb->status = ret;
+
+ spin_unlock_irqrestore(&urb->lock, flags);
+
if (ret == -EINPROGRESS)
- return 0;
+ ret = 0;
+ else
+ uhci_unlink_generic(urb);
return ret;
}
/*
* Return the result of a transfer
+ *
+ * Must be called with urblist_lock acquired
*/
static void uhci_transfer_result(urb_t *urb)
{
urb_t *turb;
int proceed = 0, is_ring = 0;
int ret = -EINVAL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&urb->lock, flags);
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
@@ -1173,12 +1105,17 @@ static void uhci_transfer_result(urb_t *urb)
}
urb->status = ret;
- if (urb->status == -EINPROGRESS)
+
+ spin_unlock_irqrestore(&urb->lock, flags);
+
+ if (ret == -EINPROGRESS)
return;
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
- uhci_unlink_control(urb);
+ case PIPE_BULK:
+ case PIPE_ISOCHRONOUS:
+ uhci_unlink_generic(urb);
break;
case PIPE_INTERRUPT:
/* Interrupts are an exception */
@@ -1186,14 +1123,8 @@ static void uhci_transfer_result(urb_t *urb)
if (urb->interval)
uhci_reset_interrupt(urb);
else
- uhci_unlink_interrupt(urb);
- return;
- case PIPE_BULK:
- uhci_unlink_bulk(urb);
- break;
- case PIPE_ISOCHRONOUS:
- uhci_unlink_isochronous(urb);
- break;
+ uhci_unlink_generic(urb);
+ return; /* <-- Note the return */
}
if (urb->next) {
@@ -1231,10 +1162,32 @@ static void uhci_transfer_result(urb_t *urb)
}
}
+static int uhci_unlink_generic(urb_t *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+ struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+ if (!urbp)
+ return -EINVAL;
+
+ uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
+
+ uhci_remove_urb_list(uhci, urb);
+
+ if (urbp->qh)
+ /* The interrupt loop will reclaim the QH's */
+ uhci_remove_qh(uhci, urbp->qh);
+
+ uhci_destroy_urb_priv(urb);
+
+ return 0;
+}
+
static int uhci_unlink_urb(urb_t *urb)
{
struct uhci *uhci;
int ret = 0;
+ unsigned long flags;
if (!urb)
return -EINVAL;
@@ -1244,40 +1197,34 @@ static int uhci_unlink_urb(urb_t *urb)
uhci = (struct uhci *)urb->dev->bus->hcpriv;
+ /* Short circuit the virtual root hub */
if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
return rh_unlink_urb(urb);
if (urb->status == -EINPROGRESS) {
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_CONTROL:
- ret = uhci_unlink_control(urb);
- break;
- case PIPE_INTERRUPT:
- ret = uhci_unlink_interrupt(urb);
- break;
- case PIPE_BULK:
- ret = uhci_unlink_bulk(urb);
- break;
- case PIPE_ISOCHRONOUS:
- ret = uhci_unlink_isochronous(urb);
- break;
- }
+ uhci_unlink_generic(urb);
- if (urb->complete)
- urb->complete(urb);
+ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ spin_lock_irqsave(&uhci->urb_remove_lock, flags);
+ list_add(&urb->urb_list, &uhci->urb_remove_list);
+ spin_unlock_irqrestore(&uhci->urb_remove_lock, flags);
-#ifndef CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
- if (in_interrupt()) { /* wait at least 1 frame */
- int errorcount = 10;
+ urb->status = -ECONNABORTED;
+ } else {
+ if (in_interrupt()) { /* wait at least 1 frame */
+ static int errorcount = 10;
- if (errorcount--)
- dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
- udelay(1000);
- } else
- schedule_timeout(1+1*HZ/1000);
-#endif
+ if (errorcount--)
+ dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
+ udelay(1000);
+ } else
+ schedule_timeout(1+1*HZ/1000);
- urb->status = -ENOENT;
+ if (urb->complete)
+ urb->complete(urb);
+
+ urb->status = -ENOENT;
+ }
}
return ret;
@@ -1412,9 +1359,12 @@ static int rh_init_int_timer(urb_t *urb);
static void rh_int_timer_do(unsigned long ptr)
{
- int len;
- urb_t *urb = (urb_t *)ptr;
+ urb_t *urb = (urb_t *)ptr, *u;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+ struct list_head *tmp, *head = &uhci->urb_list;
+ struct urb_priv *urbp;
+ int len;
+ unsigned long flags;
if (uhci->rh.send) {
len = rh_send_irq(urb);
@@ -1425,6 +1375,21 @@ static void rh_int_timer_do(unsigned long ptr)
}
}
+ nested_lock(&uhci->urblist_lock, flags);
+ tmp = head->next;
+ while (tmp != head) {
+ u = list_entry(tmp, urb_t, urb_list);
+
+ urbp = (struct urb_priv *)u->hcpriv;
+ if (urbp) {
+ if (urbp->fsbr && time_after(jiffies, urbp->inserttime + IDLE_TIMEOUT))
+ uhci_dec_fsbr(uhci, u);
+ }
+
+ tmp = tmp->next;
+ }
+ nested_unlock(&uhci->urblist_lock, flags);
+
rh_init_int_timer(urb);
}
@@ -1645,6 +1610,27 @@ static int rh_unlink_urb(urb_t *urb)
}
/*-------------------------------------------------------------------*/
+void uhci_free_pending_qhs(struct uhci *uhci)
+{
+ struct list_head *tmp, *head;
+ unsigned long flags;
+
+ /* Free any pending QH's */
+ spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+ head = &uhci->qh_remove_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
+
+ tmp = tmp->next;
+
+ list_del(&qh->remove_list);
+
+ uhci_free_qh(qh);
+ }
+ spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+}
+
static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
{
struct uhci *uhci = __uhci;
@@ -1652,7 +1638,6 @@ static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
unsigned short status;
unsigned long flags;
struct list_head *tmp, *head;
- urb_t *urb;
/*
* Read the interrupt status, and write it back to clear the
@@ -1676,55 +1661,36 @@ static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
}
}
- /* Walk the list of pending TD's to see which ones completed.. */
- nested_lock(&uhci->irqlist_lock, flags);
- head = &uhci->interrupt_list;
+ uhci_free_pending_qhs(uhci);
+
+ spin_lock(&uhci->urb_remove_lock);
+ head = &uhci->urb_remove_list;
tmp = head->next;
while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list);
-
- urb = td->urb;
+ struct urb *urb = list_entry(tmp, struct urb, urb_list);
tmp = tmp->next;
- /* Checks the status and does all of the magic necessary */
- uhci_transfer_result(urb);
- }
- nested_unlock(&uhci->irqlist_lock, flags);
-}
-
-static void uhci_stop_hc_schedule(struct uhci *uhci)
-{
-#ifdef CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
- unsigned int cmdreg, timeout = 1000;
-
- cmdreg = inw(uhci->io_addr + USBCMD);
- outw(cmdreg & ~USBCMD_RS, uhci->io_addr + USBCMD);
+ list_del(&urb->urb_list);
- while (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) {
- if (!--timeout) {
- printk(KERN_ERR "uhci: stop_hc_schedule failed, HC still running\n");
- break;
- }
+ if (urb->complete)
+ urb->complete(urb);
}
-#endif
-}
+ spin_unlock(&uhci->urb_remove_lock);
-static void uhci_start_hc_schedule(struct uhci *uhci)
-{
-#ifdef CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
- unsigned int cmdreg, timeout = 1000;
+ /* Walk the list of pending TD's to see which ones completed */
+ nested_lock(&uhci->urblist_lock, flags);
+ head = &uhci->urb_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb *urb = list_entry(tmp, struct urb, urb_list);
- cmdreg = inw(uhci->io_addr + USBCMD);
- outw(cmdreg | USBCMD_RS, uhci->io_addr + USBCMD);
+ tmp = tmp->next;
- while (inw(uhci->io_addr + USBSTS) & USBSTS_HCH) {
- if (!--timeout) {
- printk(KERN_ERR "uhci: start_hc_schedule failed, HC still halted\n");
- break;
- }
+ /* Checks the status and does all of the magic necessary */
+ uhci_transfer_result(urb);
}
-#endif
+ nested_unlock(&uhci->urblist_lock, flags);
}
static void reset_hc(struct uhci *uhci)
@@ -1780,7 +1746,7 @@ static void start_hc(struct uhci *uhci)
* of the queues. We don't do that here, because
* we'll create the actual TD entries on demand.
* - The first queue is the "interrupt queue".
- * - The second queue is the "control queue".
+ * - The second queue is the "control queue", split into low and high speed
* - The third queue is "bulk data".
*/
static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size)
@@ -1799,14 +1765,16 @@ static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size)
uhci->io_addr = io_addr;
uhci->io_size = io_size;
- INIT_LIST_HEAD(&uhci->interrupt_list);
- INIT_LIST_HEAD(&uhci->urb_list);
+ spin_lock_init(&uhci->qh_remove_lock);
+ INIT_LIST_HEAD(&uhci->qh_remove_list);
+
+ spin_lock_init(&uhci->urb_remove_lock);
+ INIT_LIST_HEAD(&uhci->urb_remove_list);
- spin_lock_init(&uhci->framelist_lock);
nested_init(&uhci->urblist_lock);
- nested_init(&uhci->irqlist_lock);
+ INIT_LIST_HEAD(&uhci->urb_list);
- uhci->fsbr = 0;
+ spin_lock_init(&uhci->framelist_lock);
/* We need exactly one page (per UHCI specs), how convenient */
/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
@@ -1870,8 +1838,12 @@ static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size)
uhci->skel_bulk_qh.link = virt_to_bus(&uhci->skel_term_qh) | UHCI_PTR_QH;
uhci->skel_bulk_qh.element = UHCI_PTR_TERM;
+ /* This dummy TD is to work around a bug in Intel PIIX controllers */
+ uhci_fill_td(&uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+ uhci->skel_term_td.link = UHCI_PTR_TERM;
+
uhci->skel_term_qh.link = UHCI_PTR_TERM;
- uhci->skel_term_qh.element = UHCI_PTR_TERM;
+ uhci->skel_term_qh.element = virt_to_bus(&uhci->skel_term_td);
/*
* Fill the frame list: make all entries point to
@@ -2022,23 +1994,24 @@ static int found_uhci(struct pci_dev *dev)
dev->resource[i].end - dev->resource[i].start + 1;
/* IO address? */
- if (!(dev->resource[i].flags & 1))
+ if (!(dev->resource[i].flags & IORESOURCE_IO))
continue;
/* Is it already in use? */
if (check_region(io_addr, io_size))
break;
- /* disable legacy emulation */
- pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
-
- pci_enable_device(dev);
-
if (!dev->irq) {
err("found UHCI device with no IRQ assigned. check BIOS settings!");
continue;
}
+ /* disable legacy emulation */
+ pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+ if (pci_enable_device(dev) < 0)
+ continue;
+
return setup_uhci(dev, dev->irq, io_addr, io_size);
}
@@ -2081,6 +2054,12 @@ int uhci_init(void)
if (!uhci_qh_cachep)
goto qh_failed;
+ uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
+ sizeof(struct urb_priv), 0, 0, NULL, NULL);
+
+ if (!uhci_up_cachep)
+ goto up_failed;
+
retval = -ENODEV;
dev = NULL;
for (;;) {
@@ -2105,6 +2084,10 @@ int uhci_init(void)
return 0;
init_failed:
+ if (kmem_cache_destroy(uhci_up_cachep))
+ printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+up_failed:
if (kmem_cache_destroy(uhci_qh_cachep))
printk(KERN_INFO "uhci: not all QH's were freed\n");
@@ -2118,13 +2101,13 @@ td_failed:
void uhci_cleanup(void)
{
- struct list_head *next, *tmp, *head = &uhci_list;
+ struct list_head *tmp, *head = &uhci_list;
tmp = head->next;
while (tmp != head) {
struct uhci *uhci = list_entry(tmp, struct uhci, uhci_list);
- next = tmp->next;
+ tmp = tmp->next;
list_del(&uhci->uhci_list);
INIT_LIST_HEAD(&uhci->uhci_list);
@@ -2137,11 +2120,14 @@ void uhci_cleanup(void)
reset_hc(uhci);
release_region(uhci->io_addr, uhci->io_size);
- release_uhci(uhci);
+ uhci_free_pending_qhs(uhci);
- tmp = next;
+ release_uhci(uhci);
}
+ if (kmem_cache_destroy(uhci_up_cachep))
+ printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
if (kmem_cache_destroy(uhci_qh_cachep))
printk(KERN_INFO "uhci: not all QH's were freed\n");