📄 ohci-q.c
字号:
/* * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> * * This file is licenced under the GPL. * $Id: ohci-q.c,v 1.8 2002/03/27 20:57:01 dbrownell Exp $ */static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv){ int last = urb_priv->length - 1; if (last >= 0) { int i; struct td *td = urb_priv->td [0];#ifdef CONFIG_PCI int len = td->urb->transfer_buffer_length; int dir = usb_pipeout (td->urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE; /* unmap CTRL URB setup buffer (always td 0) */ if (usb_pipecontrol (td->urb->pipe)) { pci_unmap_single (hc->hcd.pdev, td->data_dma, 8, PCI_DMA_TODEVICE); /* CTRL data buffer starts at td 1 if len > 0 */ if (len && last > 0) td = urb_priv->td [1]; } /* else: ISOC, BULK, INTR data buffer starts at td 0 */ /* unmap data buffer */ if (len && td->data_dma) pci_unmap_single (hc->hcd.pdev, td->data_dma, len, dir);#else# warning "assuming no buffer unmapping is needed"#endif for (i = 0; i <= last; i++) { td = urb_priv->td [i]; if (td) td_free (hc, td); } } kfree (urb_priv);}/*-------------------------------------------------------------------------*//* * URB goes back to driver, and isn't reissued. * It's completely gone from HC data structures. * PRECONDITION: no locks held (Giveback can call into HCD.) */static void finish_urb (struct ohci_hcd *ohci, struct urb *urb){ unsigned long flags;#ifdef DEBUG if (!urb->hcpriv) { err ("already unlinked!"); BUG (); }#endif urb_free_priv (ohci, urb->hcpriv); urb->hcpriv = NULL; spin_lock_irqsave (&urb->lock, flags); if (likely (urb->status == -EINPROGRESS)) urb->status = 0; spin_unlock_irqrestore (&urb->lock, flags);#ifdef OHCI_VERBOSE_DEBUG urb_print (urb, "RET", usb_pipeout (urb->pipe));#endif usb_hcd_giveback_urb (&ohci->hcd, urb);}static void td_submit_urb (struct urb *urb);/* Report interrupt transfer completion, maybe reissue */static void intr_resub (struct ohci_hcd *hc, struct urb *urb){ urb_priv_t *urb_priv = urb->hcpriv; unsigned long flags;#ifdef CONFIG_PCI// FIXME rewrite this resubmit path. use pci_dma_sync_single()// and requeue more cheaply, and only if needed.// Better yet ... abolish the notion of automagic resubmission. pci_unmap_single (hc->hcd.pdev, urb_priv->td [0]->data_dma, urb->transfer_buffer_length, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);#endif /* FIXME: MP race. If another CPU partially unlinks * this URB (urb->status was updated, hasn't yet told * us to dequeue) before we call complete() here, an * extra "unlinked" completion will be reported... */ spin_lock_irqsave (&urb->lock, flags); if (likely (urb->status == -EINPROGRESS)) urb->status = 0; spin_unlock_irqrestore (&urb->lock, flags);#ifdef OHCI_VERBOSE_DEBUG urb_print (urb, "INTR", usb_pipeout (urb->pipe));#endif urb->complete (urb); /* always requeued, but ED_SKIP if complete() unlinks. * EDs are removed from periodic table only at SOF intr. */ urb->actual_length = 0; spin_lock_irqsave (&urb->lock, flags); if (urb_priv->state != URB_DEL) urb->status = -EINPROGRESS; spin_unlock (&urb->lock); spin_lock (&hc->lock); td_submit_urb (urb); spin_unlock_irqrestore (&hc->lock, flags);}/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*/ /* search for the right branch to insert an interrupt ed into the int tree * do some load balancing; * returns the branch and * sets the interval to interval = 2^integer (ld (interval)) */static int ep_int_balance (struct ohci_hcd *ohci, int interval, int load){ int i, branch = 0; /* search for the least loaded interrupt endpoint branch */ for (i = 0; i < NUM_INTS ; i++) if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; branch = branch % interval; for (i = branch; i < NUM_INTS; i += interval) ohci->ohci_int_load [i] += load; return branch;}/*-------------------------------------------------------------------------*//* 2^int ( ld (inter)) */static int ep_2_n_interval (int inter){ int i; for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++) continue; return 1 << i;}/*-------------------------------------------------------------------------*//* the int tree is a binary tree * in order to process it sequentially the indexes of the branches have * to be mapped the mapping reverses the bits of a word of num_bits length */static int ep_rev (int num_bits, int word){ int i, wout = 0; for (i = 0; i < num_bits; i++) wout |= (( (word >> i) & 1) << (num_bits - i - 1)); return wout;}/*-------------------------------------------------------------------------*//* link an ed into one of the HC chains */static int ep_link (struct ohci_hcd *ohci, struct ed *edi){ int int_branch, i; int inter, interval, load; __u32 *ed_p; volatile struct ed *ed = edi; ed->state = ED_OPER; switch (ed->type) { case PIPE_CONTROL: ed->hwNextED = 0; if (ohci->ed_controltail == NULL) { writel (ed->dma, &ohci->regs->ed_controlhead); } else { ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma); } ed->ed_prev = ohci->ed_controltail; if (!ohci->ed_controltail && !ohci->ed_rm_list [0] && !ohci->ed_rm_list [1] && !ohci->sleeping ) { ohci->hc_control |= OHCI_CTRL_CLE; writel (ohci->hc_control, &ohci->regs->control); } ohci->ed_controltail = edi; break; case PIPE_BULK: ed->hwNextED = 0; if (ohci->ed_bulktail == NULL) { writel (ed->dma, &ohci->regs->ed_bulkhead); } else { ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma); } ed->ed_prev = ohci->ed_bulktail; if (!ohci->ed_bulktail && !ohci->ed_rm_list [0] && !ohci->ed_rm_list [1] && !ohci->sleeping ) { ohci->hc_control |= OHCI_CTRL_BLE; writel (ohci->hc_control, &ohci->regs->control); } ohci->ed_bulktail = edi; break; case PIPE_INTERRUPT: load = ed->int_load; interval = ep_2_n_interval (ed->int_period); ed->int_interval = interval; int_branch = ep_int_balance (ohci, interval, load); ed->int_branch = int_branch; for (i = 0; i < ep_rev (6, interval); i += inter) { inter = 1; for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]); (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 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); ed->hwNextED = *ed_p; *ed_p = cpu_to_le32 (ed->dma); }#ifdef OHCI_VERBOSE_DEBUG ohci_dump_periodic (ohci, "LINK_INT");#endif break; case PIPE_ISOCHRONOUS: ed->hwNextED = 0; ed->int_interval = 1; if (ohci->ed_isotail != NULL) { ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma); ed->ed_prev = ohci->ed_isotail; } else { for ( i = 0; i < NUM_INTS; i += inter) { inter = 1; 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); *ed_p = cpu_to_le32 (ed->dma); } ed->ed_prev = NULL; } ohci->ed_isotail = edi; #ifdef OHCI_VERBOSE_DEBUG ohci_dump_periodic (ohci, "LINK_ISO");#endif break; } return 0;}/*-------------------------------------------------------------------------*//* scan the periodic table to find and unlink this ED */static void periodic_unlink ( struct ohci_hcd *ohci, struct ed *ed, unsigned index, unsigned period) { for (; index < NUM_INTS; index += period) { __u32 *ed_p = &ohci->hcca->int_table [index]; 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 * so the HC can eventually finish the processing of the unlinked ed */static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) { int i; ed->hwINFO |= ED_SKIP; switch (ed->type) { case PIPE_CONTROL: if (ed->ed_prev == NULL) { if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_CLE; writel (ohci->hc_control, &ohci->regs->control); } writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; } if (ohci->ed_controltail == ed) { ohci->ed_controltail = ed->ed_prev; } else { (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) ->ed_prev = ed->ed_prev; } break; case PIPE_BULK: if (ed->ed_prev == NULL) { if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_BLE; writel (ohci->hc_control, &ohci->regs->control); } writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; } if (ohci->ed_bulktail == ed) { ohci->ed_bulktail = ed->ed_prev; } else { (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) ->ed_prev = ed->ed_prev; } break; case PIPE_INTERRUPT: periodic_unlink (ohci, ed, ed->int_branch, ed->int_interval); for (i = ed->int_branch; i < NUM_INTS; i += ed->int_interval) ohci->ohci_int_load [i] -= ed->int_load;#ifdef OHCI_VERBOSE_DEBUG ohci_dump_periodic (ohci, "UNLINK_INT");#endif break; case PIPE_ISOCHRONOUS: 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; if (ed->ed_prev != NULL) ed->ed_prev->hwNextED = ed->hwNextED; else periodic_unlink (ohci, ed, 0, 1);#ifdef OHCI_VERBOSE_DEBUG ohci_dump_periodic (ohci, "UNLINK_ISO");#endif break; } /* FIXME ED's "unlink" state is indeterminate; * the HC might still be caching it (till SOF). */ ed->state = ED_UNLINK; return 0;}/*-------------------------------------------------------------------------*//* (re)init an endpoint; this _should_ be done once at the * usb_set_configuration command, but the USB stack is a bit stateless * so we do it at every transaction. * if the state of the ed is ED_NEW then a dummy td is added and the * state is changed to ED_UNLINK * in all other cases the state is left unchanged * the ed info fields are set even though most of them should * not change */static struct ed *ep_add_ed ( struct usb_device *udev, unsigned int pipe, int interval, int load, int mem_flags) { struct ohci_hcd *ohci = hcd_to_ohci (udev->bus->hcpriv); struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv; struct td *td; struct ed *ed; unsigned ep; unsigned long flags; spin_lock_irqsave (&ohci->lock, flags); ep = usb_pipeendpoint (pipe) << 1; if (!usb_pipecontrol (pipe) && usb_pipeout (pipe)) ep |= 1; if (!(ed = dev->ep [ep])) { ed = ed_alloc (ohci, SLAB_ATOMIC); if (!ed) { /* out of memory */ spin_unlock_irqrestore (&ohci->lock, flags); return NULL; } dev->ep [ep] = ed; } if (ed->state & ED_URB_DEL) { /* pending unlink request */ spin_unlock_irqrestore (&ohci->lock, flags); return NULL; } if (ed->state == ED_NEW) { ed->hwINFO = ED_SKIP; /* dummy td; end of td list for ed */ td = td_alloc (ohci, SLAB_ATOMIC); if (!td) { /* out of memory */ spin_unlock_irqrestore (&ohci->lock, flags); return NULL; } ed->hwTailP = cpu_to_le32 (td->td_dma); ed->hwHeadP = ed->hwTailP; ed->state = ED_UNLINK; ed->type = usb_pipetype (pipe); }// FIXME: don't do this if it's linked to the HC,// we might clobber data toggle or other state ... ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) | usb_pipeendpoint (pipe) << 7 | (usb_pipeisoc (pipe)? 0x8000: 0) | (usb_pipecontrol (pipe) ? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) | (udev->speed == USB_SPEED_LOW) << 13 | usb_maxpacket (udev, pipe, usb_pipeout (pipe)) << 16); if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { ed->int_period = interval; ed->int_load = load; } spin_unlock_irqrestore (&ohci->lock, flags); return ed; }/*-------------------------------------------------------------------------*//* request unlinking of an endpoint from an operational HC. * put the ep on the rm_list and stop the bulk or ctrl list * real work is done at the next start frame (SF) hardware interrupt */static void ed_unlink (struct usb_device *usb_dev, struct ed *ed){ unsigned int frame; struct ohci_hcd *ohci = hcd_to_ohci (usb_dev->bus->hcpriv); /* already pending? */ if (ed->state & ED_URB_DEL) return; ed->state |= ED_URB_DEL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -