📄 usb-uhci.c
字号:
qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // arm QH if (!bulk_urb) { // new bulk queue if (urb->transfer_flags & USB_QUEUE_BULK) { spin_lock (&s->td_lock); // both QHs in one go insert_qh (s, s->chain_end, qh, 0); // Main QH insert_qh (s, s->chain_end, nqh, 0); // Helper QH spin_unlock (&s->td_lock); } else insert_qh (s, s->chain_end, qh, 0); } //uhci_show_queue(s->bulk_chain); //dbg("uhci_submit_bulk_urb: exit\n"); return 0;}/*-------------------------------------------------------------------*/_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv){ struct list_head *p; uhci_desc_t *td; for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) { td = list_entry (p, uhci_desc_t, desc_list); unlink_td (s, td, 1); }}/*-------------------------------------------------------------------*/_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv){ struct list_head *p; uhci_desc_t *td; while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { td = list_entry (p, uhci_desc_t, desc_list); list_del (p); delete_desc (s, td); }}/*-------------------------------------------------------------------*//* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink) CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark) CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink) looks a bit complicated because of all the bulk queueing goodies*/_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode){ uhci_desc_t *bqh, *nqh, *prevqh, *prevtd; int now; urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; now=UHCI_GET_CURRENT_FRAME(s); bqh=priv->bottom_qh; if (!priv->next_queued_urb) { // no more appended bulk queues queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", urb, qh, bqh, priv->next_qh); if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) { // qh not top of the queue unsigned long flags; urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; spin_lock_irqsave (&s->qh_lock, flags); prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical); set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh mb(); queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd); spin_unlock_irqrestore (&s->qh_lock, flags); ppriv->bottom_qh = priv->bottom_qh; ppriv->next_queued_urb = NULL; } else { // queue is dead, qh is top of the queue if (mode != CLEAN_TRANSFER_DELETION_MARK) unlink_qh(s, qh); // remove qh from horizontal chain if (bqh) { // remove remainings of bulk queue nqh=priv->next_qh; if (mode != CLEAN_TRANSFER_DELETION_MARK) unlink_qh(s, nqh); // remove nqh from horizontal chain if (mode != CLEAN_TRANSFER_NO_DELETION) { // add helper QHs to free desc list nqh->last_used = bqh->last_used = now; list_add_tail (&nqh->horizontal, &s->free_desc); list_add_tail (&bqh->horizontal, &s->free_desc); } } } } else { // there are queued urbs following queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p", urb, priv->prev_queued_urb, priv->next_queued_urb, qh, bqh, priv->next_qh); if (mode != CLEAN_TRANSFER_DELETION_MARK) { // no work for cleanup at unlink-completion struct urb *nurb; unsigned long flags; nurb = priv->next_queued_urb; spin_lock_irqsave (&s->qh_lock, flags); if (!priv->prev_queued_urb) { // top QH prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal); set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); list_del (&qh->horizontal); // remove this qh form horizontal chain list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain } else { // intermediate QH urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv; uhci_desc_t * bnqh; bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list); ppriv->bottom_qh = bnqh; ppriv->next_queued_urb = nurb; prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); } mb(); ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb; spin_unlock_irqrestore (&s->qh_lock, flags); } } if (mode != CLEAN_TRANSFER_NO_DELETION) { qh->last_used = now; list_add_tail (&qh->horizontal, &s->free_desc); // mark qh for later deletion/kfree }}/*-------------------------------------------------------------------*/// Release bandwidth for Interrupt or Isoc. transfers _static void uhci_release_bandwidth(struct urb *urb){ if (urb->bandwidth) { switch (usb_pipetype(urb->pipe)) { case PIPE_INTERRUPT: usb_release_bandwidth (urb->dev, urb, 0); break; case PIPE_ISOCHRONOUS: usb_release_bandwidth (urb->dev, urb, 1); break; default: break; } } }_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv){ if (urb_priv->setup_packet_dma) pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma, sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); if (urb_priv->transfer_buffer_dma) pci_dma_sync_single(s->uhci_pci, urb_priv->transfer_buffer_dma, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);}_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv){ if (urb_priv->setup_packet_dma) { pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma, sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); urb_priv->setup_packet_dma = 0; } if (urb_priv->transfer_buffer_dma) { pci_unmap_single(s->uhci_pci, urb_priv->transfer_buffer_dma, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); urb_priv->transfer_buffer_dma = 0; }}/*-------------------------------------------------------------------*//* needs urb_list_lock! mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list*/_static int uhci_unlink_urb_async (uhci_t *s,struct urb *urb, int mode){ uhci_desc_t *qh; urb_priv_t *urb_priv; async_dbg("unlink_urb_async called %p",urb); if ((urb->status == -EINPROGRESS) || ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags)) { ((urb_priv_t*)urb->hcpriv)->started = ~0; // mark dequeue_urb (s, urb); if (mode==UNLINK_ASYNC_STORE_URB) list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb uhci_switch_timer_int(s); s->unlink_urb_done = 1; uhci_release_bandwidth(urb); urb->status = -ECONNABORTED; // mark urb as "waiting to be killed" urb_priv = (urb_priv_t*)urb->hcpriv; switch (usb_pipetype (urb->pipe)) { case PIPE_INTERRUPT: usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); case PIPE_ISOCHRONOUS: uhci_clean_iso_step1 (s, urb_priv); break; case PIPE_BULK: case PIPE_CONTROL: qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); uhci_clean_transfer (s, urb, qh, CLEAN_TRANSFER_NO_DELETION); break; } ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s); return -EINPROGRESS; // completion will follow } return 0; // URB already dead}/*-------------------------------------------------------------------*/// kills an urb by unlinking descriptors and waiting for at least one frame_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb){ uhci_desc_t *qh; urb_priv_t *urb_priv; unsigned long flags=0; struct usb_device *usb_dev; spin_lock_irqsave (&s->urb_list_lock, flags); if (urb->status == -EINPROGRESS) { // move descriptors out of the running chains, dequeue urb uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_DONT_STORE); urb_priv = urb->hcpriv; urb->status = -ENOENT; // prevent from double deletion after unlock spin_unlock_irqrestore (&s->urb_list_lock, flags); // cleanup the rest switch (usb_pipetype (urb->pipe)) { case PIPE_INTERRUPT: case PIPE_ISOCHRONOUS: uhci_wait_ms(1); uhci_clean_iso_step2(s, urb_priv); break; case PIPE_BULK: case PIPE_CONTROL: qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK); uhci_wait_ms(1); } urb->status = -ENOENT; // mark urb as killed uhci_urb_dma_unmap(s, urb, urb->hcpriv);#ifdef DEBUG_SLAB kmem_cache_free (urb_priv_kmem, urb->hcpriv);#else kfree (urb->hcpriv);#endif usb_dev = urb->dev; if (urb->complete) { dbg("unlink_urb: calling completion"); urb->dev = NULL; urb->complete ((struct urb *) urb); } usb_dec_dev_use (usb_dev); } else spin_unlock_irqrestore (&s->urb_list_lock, flags); return 0;}/*-------------------------------------------------------------------*/// async unlink_urb completion/cleanup work// has to be protected by urb_list_lock!// features: if set in transfer_flags, the resulting status of the killed// transaction is not overwritten_static void uhci_cleanup_unlink(uhci_t *s, int force){ struct list_head *q; struct urb *urb; struct usb_device *dev; int now, type; urb_priv_t *urb_priv; q=s->urb_unlinked.next; now=UHCI_GET_CURRENT_FRAME(s); while (q != &s->urb_unlinked) { urb = list_entry (q, struct urb, urb_list); urb_priv = (urb_priv_t*)urb->hcpriv; q = urb->urb_list.next; if (!urb_priv) // avoid crash when URB is corrupted break; if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) { async_dbg("async cleanup %p",urb); type=usb_pipetype (urb->pipe); switch (type) { // process descriptors case PIPE_CONTROL: process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) break; case PIPE_BULK: if (!s->avoid_bulk.counter) process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) else continue; break; case PIPE_ISOCHRONOUS: process_iso (s, urb, PROCESS_ISO_FORCE); // force, don't unlink break; case PIPE_INTERRUPT: process_interrupt (s, urb); break; } if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) urb->status = -ECONNRESET; // mark as asynchronously killed dev = urb->dev; // completion may destroy all... urb_priv = urb->hcpriv; list_del (&urb->urb_list); uhci_urb_dma_sync(s, urb, urb_priv); if (urb->complete) { spin_unlock(&s->urb_list_lock); urb->dev = NULL; urb->complete ((struct urb *) urb); spin_lock(&s->urb_list_lock); } if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) urb->status = -ENOENT; // now the urb is really dead switch (type) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: uhci_clean_iso_step2(s, urb_priv); break; } uhci_urb_dma_unmap(s, urb, urb_priv); usb_dec_dev_use (dev);#ifdef DEBUG_SLAB kmem_cache_free (urb_priv_kmem, urb_priv);#else kfree (urb_priv);#endif } }} /*-------------------------------------------------------------------*/_static int uhci_unlink_urb (struct urb *urb){ uhci_t *s; unsigned long flags=0; dbg("uhci_unlink_urb called for %p",urb); if (!urb || !urb->dev) // you never know... return -EINVAL; s = (uhci_t*) urb->dev->bus->hcpriv; if (usb_pipedevice (urb->pipe) == s->rh.devnum) return rh_unlink_urb (urb); if (!urb->hcpriv) return -EINVAL; if (urb->transfer_flags & USB_ASYNC_UNLINK) { int ret; spin_lock_irqsave (&s->urb_list_lock, flags); uhci_release_bandwidth(urb); ret = uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); spin_unlock_irqrestore (&s->urb_list_lock, flags); return ret; } else return uhci_unlink_urb_sync(s, urb);}/*-------------------------------------------------------------------*/// In case of ASAP iso transfer, search the URB-list for already queued URBs// for this EP and calculate the earliest start frame for the new// URB (easy seamless URB continuation!)_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end){ struct urb *u, *last_urb = NULL; uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; struct list_head *p; int ret=-1; unsigned long flags; spin_lock_irqsave (&s->urb_list_lock, flags); p=s->urb_list.prev; for (; p != &s->urb_list; p = p->prev) { u = list_entry (p, struct urb, urb_list); // look for pending URBs with identical pipe handle // works only because iso doesn't toggle the data bit! if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) { if (!last_urb) *start = u->start_frame; last_urb = u; } } if (last_urb) { *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; ret=0; } spin_unlock_irqrestore(&s->urb_list_lock, flags); return ret;}/*-------------------------------------------------------------------*/// adjust start_frame according to scheduling constraints (ASAP etc)_static int iso_find_start (struct urb *urb){ uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; unsigned int now; unsigned int start_limit = 0, stop_limit = 0, queued_size; int limits; now = UHCI_GET_CURRENT_FRAME (s) & 1023; if ((unsigned) urb->number_of_packets > 900) return -EFBIG; limits = find_iso_limits (urb, &start_limit, &stop_limit); queued_size = (stop_limit - start_limit) & 1023; if (urb->transfer_flags & USB_ISO_ASAP) { // first iso if (limits) { // 10ms setup should be enough //FIXME! urb->start_frame = (now + 10) & 1023; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -