diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 11d4bac4a19c8e56f714af882e9c1a27406da96b..f4bc1a75248fbc36ba4a63b695667e04071676c6 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -14,7 +14,8 @@ * - data used only by the HCD ... kmalloc is fine * - async and periodic schedules, shared by HC and HCD ... these * need to use pci_pool or pci_alloc_consistent - * - driver buffers, read/written by HC ... single shot DMA mapped + * - driver buffers, read/written by HC ... the hcd glue or the + * device driver provides us with dma addresses * * There's also PCI "register" data, which is memory mapped. * No memory seen by this driver is pagable. @@ -41,95 +42,6 @@ static void ohci_hcd_free (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -/* Recover a TD/ED using its collision chain */ -static inline void * -dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma) -{ - struct hash_t * scan = entry->head; - while (scan && scan->dma != dma) - scan = scan->next; - return scan ? scan->virt : 0; -} - -static inline struct td * -dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma) -{ - td_dma &= TD_MASK; - return (struct td *) dma_to_ed_td(&(hc->td_hash [TD_HASH_FUNC(td_dma)]), - td_dma); -} - -// FIXME: when updating the hashtables this way, mem_flags is unusable... - -/* Add a hash entry for a TD/ED; return true on success */ -static inline int -hash_add_ed_td ( - struct hash_list_t *entry, - void *virt, - dma_addr_t dma, - int mem_flags -) -{ - struct hash_t * scan; - - scan = (struct hash_t *) kmalloc (sizeof *scan, mem_flags); - if (!scan) - return 0; - - if (!entry->tail) { - entry->head = entry->tail = scan; - } else { - entry->tail->next = scan; - entry->tail = scan; - } - - scan->virt = virt; - scan->dma = dma; - scan->next = NULL; - return 1; -} - -static inline int -hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags) -{ - return hash_add_ed_td (&(hc->td_hash [TD_HASH_FUNC (td->td_dma)]), - td, td->td_dma, mem_flags); -} - - -static inline void -hash_free_ed_td (struct hash_list_t *entry, void *virt) -{ - struct hash_t *scan, *prev; - scan = prev = entry->head; - - // Find and unlink hash entry - while (scan && scan->virt != virt) { - prev = scan; - scan = scan->next; - } - if (scan) { - if (scan == entry->head) { - if (entry->head == entry->tail) - entry->head = entry->tail = NULL; - else - entry->head = scan->next; - } else if (scan == entry->tail) { - entry->tail = prev; - prev->next = NULL; - } else - prev->next = scan->next; - kfree(scan); - } -} - -static inline void -hash_free_td (struct ohci_hcd *hc, struct td * td) -{ - hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td); -} - - static int ohci_mem_init (struct ohci_hcd *ohci) { ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev, @@ -161,6 +73,21 @@ static void ohci_mem_cleanup (struct ohci_hcd *ohci) } } +/*-------------------------------------------------------------------------*/ + +/* ohci "done list" processing needs this mapping */ +static inline struct td * +dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma) +{ + struct td *td; + + td_dma &= TD_MASK; + td = hc->td_hash [TD_HASH_FUNC(td_dma)]; + while (td && td->td_dma != td_dma) + td = td->td_hash; + return td; +} + /* TDs ... */ static struct td * td_alloc (struct ohci_hcd *hc, int mem_flags) @@ -170,12 +97,17 @@ td_alloc (struct ohci_hcd *hc, int mem_flags) td = pci_pool_alloc (hc->td_cache, mem_flags, &dma); if (td) { + int hash; + + /* in case hc fetches it, make it look dead */ + memset (td, 0, sizeof *td); + td->hwNextTD = cpu_to_le32 (dma); td->td_dma = dma; + /* hash it for later reverse mapping */ - if (!hash_add_td (hc, td, mem_flags)) { - pci_pool_free (hc->td_cache, td, dma); - return NULL; - } + hash = TD_HASH_FUNC (dma); + td->td_hash = hc->td_hash [hash]; + hc->td_hash [hash] = td; } return td; } @@ -183,10 +115,18 @@ td_alloc (struct ohci_hcd *hc, int mem_flags) static void td_free (struct ohci_hcd *hc, struct td *td) { - hash_free_td (hc, td); + struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)]; + + while (*prev && *prev != td) + prev = &(*prev)->td_hash; + if (*prev) + *prev = td->td_hash; + else + dev_dbg (*hc->hcd.controller, "bad hash for td %p\n", td); pci_pool_free (hc->td_cache, td, td->td_dma); } +/*-------------------------------------------------------------------------*/ /* EDs ... */ static struct ed * diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index ed25b8c0588df707b1d48f97991e39fc8f43df55..5c2e08a4027e81a3f8def35b04392719c53c2d1d 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -111,6 +111,7 @@ struct td { /* rest are purely for the driver's use */ __u8 index; struct ed *ed; + struct td *td_hash; /* dma-->td hashtable */ struct td *next_dl_td; struct urb *urb; @@ -320,23 +321,9 @@ typedef struct urb_priv { #define URB_DEL 1 - -/* Hash struct used for TD/ED hashing */ -struct hash_t { - void *virt; - dma_addr_t dma; - struct hash_t *next; // chaining for collision cases -}; - -/* List of TD/ED hash entries */ -struct hash_list_t { - struct hash_t *head; - struct hash_t *tail; -}; - #define TD_HASH_SIZE 64 /* power'o'two */ - -#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE) +// sizeof (struct td) ~= 64 == 2^6 ... +#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE) /* @@ -373,7 +360,7 @@ struct ohci_hcd { */ struct pci_pool *td_cache; struct pci_pool *ed_cache; - struct hash_list_t td_hash [TD_HASH_SIZE]; + struct td *td_hash [TD_HASH_SIZE]; /* * driver state