diff options
Diffstat (limited to 'drivers/usb/uhci.c')
-rw-r--r-- | drivers/usb/uhci.c | 948 |
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"); |