# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.305 -> 1.306 # drivers/usb/usb-ohci.c 1.21 -> 1.22 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/04/02 david-b@pacbell.net 1.306 # USB ohci driver fixes # # - An oopsable bug affecting unlink of interrupt # transfers. Fix mirrors one done ages ago for ISO. # (Original patch by Matt Hughes) # - Better cleanup on init failure (Matthew Frederickson) # -------------------------------------------- # diff -Nru a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c --- a/drivers/usb/usb-ohci.c Wed Apr 3 10:47:57 2002 +++ b/drivers/usb/usb-ohci.c Wed Apr 3 10:47:57 2002 @@ -12,9 +12,10 @@ * * History: * + * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on + * load failure (Matthew Frederickson) * 2002/01/20 async unlink fixes: return -EINPROGRESS (per spec) and * make interrupt unlink-in-completion work (db) - * * 2001/09/19 USB_ZERO_PACKET support (Jean Tourrilhes) * 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt) * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam); @@ -1076,6 +1077,28 @@ /*-------------------------------------------------------------------------*/ +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink ( + struct ohci *ohci, + struct ed *ed, + unsigned index, + unsigned period +) { + for (; index < NUM_INTS; index += period) { + __u32 *ed_p = &ohci->hcca->int_table [index]; + + /* ED might have been unlinked through another path */ + while (*ed_p != 0) { + if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { + *ed_p = ed->hwNextED; + break; + } + ed_p = & ((dma_to_ed (ohci, + le32_to_cpup (ed_p)))->hwNextED); + } + } +} + /* unlink an ed from one of the HC chains. * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 @@ -1083,11 +1106,7 @@ static int ep_unlink (ohci_t * ohci, ed_t * ed) { - int int_branch; int i; - int inter; - int interval; - __u32 * ed_p; ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); @@ -1127,21 +1146,8 @@ break; case PIPE_INTERRUPT: - int_branch = ed->int_branch; - interval = ed->int_interval; - - for (i = 0; i < ep_rev (6, interval); i += inter) { - for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]), inter = 1; - (*ed_p != 0) && (*ed_p != ed->hwNextED); - ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), - inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) { - if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { - *ed_p = ed->hwNextED; - break; - } - } - } - for (i = int_branch; i < 32; i += interval) + periodic_unlink (ohci, ed, 0, 1); + for (i = ed->int_branch; i < 32; i += ed->int_interval) ohci->ohci_int_load[i] -= ed->int_load; #ifdef DEBUG ep_print_int_eds (ohci, "UNLINK_INT"); @@ -1152,23 +1158,13 @@ if (ohci->ed_isotail == ed) ohci->ed_isotail = ed->ed_prev; if (ed->hwNextED != 0) - (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; + (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) + ->ed_prev = ed->ed_prev; - if (ed->ed_prev != NULL) { + if (ed->ed_prev != NULL) ed->ed_prev->hwNextED = ed->hwNextED; - } else { - for (i = 0; i < 32; i++) { - for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); - *ed_p != 0; - ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) { - // inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); - if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { - *ed_p = ed->hwNextED; - break; - } - } - } - } + else + periodic_unlink (ohci, ed, 0, 1); #ifdef DEBUG ep_print_int_eds (ohci, "UNLINK_ISO"); #endif @@ -2354,7 +2350,6 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) { ohci_t * ohci; - struct usb_bus * bus; ohci = (ohci_t *) kmalloc (sizeof *ohci, GFP_KERNEL); if (!ohci) @@ -2383,14 +2378,15 @@ INIT_LIST_HEAD (&ohci->timeout_list); - bus = usb_alloc_bus (&sohci_device_operations); - if (!bus) { + ohci->bus = usb_alloc_bus (&sohci_device_operations); + if (!ohci->bus) { + pci_set_drvdata (dev, NULL); + pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca, + ohci->hcca, ohci->hcca_dma); kfree (ohci); return NULL; } - - ohci->bus = bus; - bus->hcpriv = (void *) ohci; + ohci->bus->hcpriv = (void *) ohci; return ohci; } @@ -2416,9 +2412,11 @@ ohci->irq = -1; } pci_set_drvdata(ohci->ohci_dev, NULL); - - usb_deregister_bus (ohci->bus); - usb_free_bus (ohci->bus); + if (ohci->bus) { + if (ohci->bus->busnum) + usb_deregister_bus (ohci->bus); + usb_free_bus (ohci->bus); + } list_del (&ohci->ohci_hcd_list); INIT_LIST_HEAD (&ohci->ohci_hcd_list); @@ -2566,12 +2564,14 @@ { unsigned long mem_resource, mem_len; void *mem_base; + int status; if (pci_enable_device(dev) < 0) return -ENODEV; if (!dev->irq) { err("found OHCI device with no IRQ assigned. check BIOS settings!"); + pci_disable_device (dev); return -ENODEV; } @@ -2580,19 +2580,28 @@ mem_len = pci_resource_len(dev, 0); if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { dbg ("controller already in use"); + pci_disable_device (dev); return -EBUSY; } mem_base = ioremap_nocache (mem_resource, mem_len); if (!mem_base) { err("Error mapping OHCI memory"); + release_mem_region (mem_resource, mem_len); + pci_disable_device (dev); return -EFAULT; } /* controller writes into our memory */ pci_set_master (dev); - return hc_found_ohci (dev, dev->irq, mem_base, id); + status = hc_found_ohci (dev, dev->irq, mem_base, id); + if (status < 0) { + iounmap (mem_base); + release_mem_region (mem_resource, mem_len); + pci_disable_device (dev); + } + return status; } /*-------------------------------------------------------------------------*/ @@ -2630,6 +2639,7 @@ hc_release_ohci (ohci); release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); + pci_disable_device (dev); }