📄 ehci-q.c
字号:
/* * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* this file is part of ehci-hcd.c *//*-------------------------------------------------------------------------*//* * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. * * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned * buffers needed for the larger number). We use one QH per endpoint, queue * multiple (bulk or control) urbs per endpoint. URBs may need several qtds. * A scheduled interrupt qh always (for now) has one qtd, one urb. * * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * interrupts) needs careful scheduling. Performance improvements can be * an ongoing challenge. That's in "ehci-sched.c". * * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, * or otherwise through transaction translators (TTs) in USB 2.0 hubs using * (b) special fields in qh entries or (c) split iso entries. TTs will * buffer low/full speed data so the host collects it at high speed. *//*-------------------------------------------------------------------------*//* fill a qtd, returning how much of the buffer we were able to queue up */static intqtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token){ int i, count; /* one buffer entry per 4K ... first might be short or unaligned */ qtd->hw_buf [0] = cpu_to_le32 (buf); count = 0x1000 - (buf & 0x0fff); /* rest of that page */ if (likely (len < count)) /* ... iff needed */ count = len; else { buf += 0x1000; buf &= ~0x0fff; /* per-qtd limit: from 16K to 20K (best alignment) */ for (i = 1; count < len && i < 5; i++) { u64 addr = buf; qtd->hw_buf [i] = cpu_to_le32 ((u32)addr); qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32)); buf += 0x1000; if ((count + 0x1000) < len) count += 0x1000; else count = len; } } qtd->hw_token = cpu_to_le32 ((count << 16) | token); qtd->length = count;#if 0 vdbg (" qtd_fill %p, token %8x bytes %d dma %x", qtd, le32_to_cpu (qtd->hw_token), count, qtd->hw_buf [0]);#endif return count;}/*-------------------------------------------------------------------------*//* update halted (but potentially linked) qh */static inline void qh_update (struct ehci_qh *qh, struct ehci_qtd *qtd){ qh->hw_current = 0; qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); qh->hw_alt_next = EHCI_LIST_END; /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ wmb (); qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);}/*-------------------------------------------------------------------------*/static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token){ /* count IN/OUT bytes, not SETUP (even short packets) */ if (likely (QTD_PID (token) != 2)) urb->actual_length += length - QTD_LENGTH (token); /* don't modify error codes */ if (unlikely (urb->status == -EINPROGRESS && (token & QTD_STS_HALT))) { if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */ urb->status = -EOVERFLOW; } else if (token & QTD_STS_MMF) { /* fs/ls interrupt xfer missed the complete-split */ urb->status = -EPROTO; } else if (token & QTD_STS_DBE) { urb->status = (QTD_PID (token) == 1) /* IN ? */ ? -ENOSR /* hc couldn't read data */ : -ECOMM; /* hc couldn't write data */ } else if (token & QTD_STS_XACT) { /* timeout, bad crc, wrong PID, etc; retried */ if (QTD_CERR (token)) urb->status = -EPIPE; else { dbg ("3strikes"); urb->status = -EPROTO; } /* CERR nonzero + no errors + halt --> stall */ } else if (QTD_CERR (token)) urb->status = -EPIPE; else /* unknown */ urb->status = -EPROTO; dbg ("ep %d-%s qtd token %08x --> status %d", /* devpath */ usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", token, urb->status); /* stall indicates some recovery action is needed */ if (urb->status == -EPIPE) { int pipe = urb->pipe; if (!usb_pipecontrol (pipe)) usb_endpoint_halt (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); if (urb->dev->tt && !usb_pipeint (pipe)) {err ("must CLEAR_TT_BUFFER, hub port %d%s addr %d ep %d", urb->dev->ttport, /* devpath */ urb->dev->tt->multi ? "" : " (all-ports TT)", urb->dev->devnum, usb_pipeendpoint (urb->pipe)); // FIXME something (khubd?) should make the hub // CLEAR_TT_BUFFER ASAP, it's blocking other // fs/ls requests... hub_tt_clear_buffer() ? } } }}static void ehci_urb_complete ( struct ehci_hcd *ehci, dma_addr_t addr, struct urb *urb) { if (urb->transfer_buffer_length && usb_pipein (urb->pipe)) pci_dma_sync_single (ehci->hcd.pdev, addr, urb->transfer_buffer_length, PCI_DMA_FROMDEVICE); /* cleanse status if we saw no error */ if (likely (urb->status == -EINPROGRESS)) { if (urb->actual_length != urb->transfer_buffer_length && (urb->transfer_flags & USB_DISABLE_SPD)) urb->status = -EREMOTEIO; else urb->status = 0; } /* only report unlinks once */ if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN)) urb->complete (urb);}/* urb->lock ignored from here on (hcd is done with urb) */static void ehci_urb_done ( struct ehci_hcd *ehci, dma_addr_t addr, struct urb *urb) { if (urb->transfer_buffer_length) pci_unmap_single (ehci->hcd.pdev, addr, urb->transfer_buffer_length, usb_pipein (urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); if (likely (urb->hcpriv != 0)) { qh_put (ehci, (struct ehci_qh *) urb->hcpriv); urb->hcpriv = 0; } if (likely (urb->status == -EINPROGRESS)) { if (urb->actual_length != urb->transfer_buffer_length && (urb->transfer_flags & USB_DISABLE_SPD)) urb->status = -EREMOTEIO; else urb->status = 0; } /* hand off urb ownership */ usb_hcd_giveback_urb (&ehci->hcd, urb);}/* * Process completed qtds for a qh, issuing completions if needed. * When freeing: frees qtds, unmaps buf, returns URB to driver. * When not freeing (queued periodic qh): retain qtds, mapping, and urb. * Races up to qh->hw_current; returns number of urb completions. */static intqh_completions ( struct ehci_hcd *ehci, struct ehci_qh *qh, int freeing) { struct ehci_qtd *qtd, *last; struct list_head *next, *qtd_list = &qh->qtd_list; int unlink = 0, halted = 0; unsigned long flags; int retval = 0; spin_lock_irqsave (&ehci->lock, flags); if (unlikely (list_empty (qtd_list))) { spin_unlock_irqrestore (&ehci->lock, flags); return retval; } /* scan QTDs till end of list, or we reach an active one */ for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list), last = 0, next = 0; next != qtd_list; last = qtd, qtd = list_entry (next, struct ehci_qtd, qtd_list)) { struct urb *urb = qtd->urb; u32 token = 0; /* clean up any state from previous QTD ...*/ if (last) { if (likely (last->urb != urb)) { /* complete() can reenter this HCD */ spin_unlock_irqrestore (&ehci->lock, flags); if (likely (freeing != 0)) ehci_urb_done (ehci, last->buf_dma, last->urb); else ehci_urb_complete (ehci, last->buf_dma, last->urb); spin_lock_irqsave (&ehci->lock, flags); retval++; } /* qh overlays can have HC's old cached copies of * next qtd ptrs, if an URB was queued afterwards. */ if (cpu_to_le32 (last->qtd_dma) == qh->hw_current && last->hw_next != qh->hw_qtd_next) { qh->hw_alt_next = last->hw_alt_next; qh->hw_qtd_next = last->hw_next; } if (likely (freeing != 0)) ehci_qtd_free (ehci, last); last = 0; } next = qtd->qtd_list.next; /* QTDs at tail may be active if QH+HC are running, * or when unlinking some urbs queued to this QH */ token = le32_to_cpu (qtd->hw_token); halted = halted || (__constant_cpu_to_le32 (QTD_STS_HALT) & qh->hw_token) != 0 || (ehci->hcd.state == USB_STATE_HALT) || (qh->qh_state == QH_STATE_IDLE); /* fault: unlink the rest, since this qtd saw an error? */ if (unlikely ((token & QTD_STS_HALT) != 0)) { freeing = unlink = 1; /* status copied below */ /* QH halts only because of fault (above) or unlink (here). */ } else if (unlikely (halted != 0)) { /* unlinking everything because of HC shutdown? */ if (ehci->hcd.state == USB_STATE_HALT) { freeing = unlink = 1; /* explicit unlink, maybe starting here? */ } else if (qh->qh_state == QH_STATE_IDLE && (urb->status == -ECONNRESET || urb->status == -ENOENT)) { freeing = unlink = 1; /* QH halted to unlink urbs _after_ this? */ } else if (!unlink && (token & QTD_STS_ACTIVE) != 0) { qtd = 0; continue; } /* unlink the rest? once we start unlinking, after * a fault or explicit unlink, we unlink all later * urbs. usb spec requires that. */ if (unlink && urb->status == -EINPROGRESS) urb->status = -ECONNRESET; /* Else QH is active, so we must not modify QTDs * that HC may be working on. No more qtds to check. */ } else if (unlikely ((token & QTD_STS_ACTIVE) != 0)) { next = qtd_list; qtd = 0; continue; } spin_lock (&urb->lock); qtd_copy_status (urb, qtd->length, token); spin_unlock (&urb->lock); /* * NOTE: this won't work right with interrupt urbs that * need multiple qtds ... only the first scan of qh->qtd_list * starts at the right qtd, yet multiple scans could happen * for transfers that are scheduled across multiple uframes. * (Such schedules are not currently allowed!) */ if (likely (freeing != 0)) list_del (&qtd->qtd_list); else { /* restore everything the HC could change * from an interrupt QTD */ qtd->hw_token = (qtd->hw_token & __constant_cpu_to_le32 (0x8300)) | cpu_to_le32 (qtd->length << 16) | __constant_cpu_to_le32 (QTD_STS_ACTIVE | (EHCI_TUNE_CERR << 10)); qtd->hw_buf [0] &= ~__constant_cpu_to_le32 (0x0fff); /* this offset, and the length above, * are likely wrong on QTDs #2..N */ qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma); }#if 0 if (urb->status == -EINPROGRESS) vdbg (" qtd %p ok, urb %p, token %8x, len %d", qtd, urb, token, urb->actual_length); else vdbg ("urb %p status %d, qtd %p, token %8x, len %d", urb, urb->status, qtd, token, urb->actual_length);#endif /* SETUP for control urb? */ if (unlikely (QTD_PID (token) == 2)) pci_unmap_single (ehci->hcd.pdev, qtd->buf_dma, sizeof (struct usb_ctrlrequest), PCI_DMA_TODEVICE); } /* patch up list head? */ if (unlikely (halted && !list_empty (qtd_list))) { qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); } spin_unlock_irqrestore (&ehci->lock, flags); /* last urb's completion might still need calling */ if (likely (last != 0)) { if (likely (freeing != 0)) { ehci_urb_done (ehci, last->buf_dma, last->urb); ehci_qtd_free (ehci, last); } else ehci_urb_complete (ehci, last->buf_dma, last->urb); retval++; } return retval;}/*-------------------------------------------------------------------------*//* * reverse of qh_urb_transaction: free a list of TDs. * used for cleanup after errors, before HC sees an URB's TDs. */static void qtd_list_free ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list) { struct list_head *entry, *temp; int unmapped = 0; list_for_each_safe (entry, temp, qtd_list) { struct ehci_qtd *qtd; qtd = list_entry (entry, struct ehci_qtd, qtd_list); list_del (&qtd->qtd_list); if (unmapped != 2) { int direction; size_t size; /* for ctrl unmap twice: SETUP and DATA; * else (bulk, intr) just once: DATA */ if (!unmapped++ && usb_pipecontrol (urb->pipe)) { direction = PCI_DMA_TODEVICE; size = sizeof (struct usb_ctrlrequest); } else { direction = usb_pipein (urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE; size = qtd->urb->transfer_buffer_length; unmapped++; } if (qtd->buf_dma) pci_unmap_single (ehci->hcd.pdev, qtd->buf_dma, size, direction); } ehci_qtd_free (ehci, qtd); }}/* * create a list of filled qtds for this URB; won't link into qh. */static struct list_head *qh_urb_transaction ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *head, int flags) { struct ehci_qtd *qtd, *qtd_prev; dma_addr_t buf, map_buf; int len, maxpacket; u32 token; /* * URBs map to sequences of QTDs: one logical transaction */ qtd = ehci_qtd_alloc (ehci, flags); if (unlikely (!qtd)) return 0; qtd_prev = 0; list_add_tail (&qtd->qtd_list, head); qtd->urb = urb; token = QTD_STS_ACTIVE; token |= (EHCI_TUNE_CERR << 10); /* for split transactions, SplitXState initialized to zero */ if (usb_pipecontrol (urb->pipe)) { /* control request data is passed in the "setup" pid */ qtd->buf_dma = pci_map_single ( ehci->hcd.pdev, urb->setup_packet, sizeof (struct usb_ctrlrequest), PCI_DMA_TODEVICE); if (unlikely (!qtd->buf_dma)) goto cleanup; /* SETUP pid */ qtd_fill (qtd, qtd->buf_dma, sizeof (struct usb_ctrlrequest), token | (2 /* "setup" */ << 8)); /* ... and always at least one more pid */ token ^= QTD_TOGGLE; qtd_prev = qtd; qtd = ehci_qtd_alloc (ehci, flags); if (unlikely (!qtd)) goto cleanup; qtd->urb = urb; qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); } /* * data transfer stage: buffer setup */ len = urb->transfer_buffer_length; if (likely (len > 0)) { buf = map_buf = pci_map_single (ehci->hcd.pdev, urb->transfer_buffer, len, usb_pipein (urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); if (unlikely (!buf))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -