/* Except for control endpoints, we make hardware maintain data *toggle(likeOHCI)...here(re)initializethetoggleintheQH, *andsetthepseudo-toggleinudev.Onlyusb_clear_halt()will *everclearit.
*/ if (!(hw->hw_info1 & cpu_to_hc32(ehci, QH_TOGGLE_CTL))) { unsigned is_out, epnum;
/* if it weren't for a common silicon quirk (writing the dummy into the qh *overlay,soqh->hw_tokenwronglybecomesinactive/halted),onlyfault *recovery(includingurbdequeue)wouldneedsoftwarechangestoaQH...
*/ staticvoid
qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
{ struct ehci_qtd *qtd;
staticint qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb,
size_t length,
u32 token
)
{ int status = -EINPROGRESS;
/* count IN/OUT bytes, not SETUP (even short packets) */ if (likely(QTD_PID(token) != PID_CODE_SETUP))
urb->actual_length += length - QTD_LENGTH (token);
/* don't modify error codes */ if (unlikely(urb->unlinked)) return status;
/* force cleanup after short read; not always an error */ if (unlikely (IS_SHORT_READ (token)))
status = -EREMOTEIO;
/* serious "can't proceed" faults reported by the hardware */ if (token & QTD_STS_HALT) { if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */
status = -EOVERFLOW; /* *WhenMMFisactiveandPIDCodeisIN,queueishalted. *EHCISpecification,Table4-13.
*/
} elseif ((token & QTD_STS_MMF) &&
(QTD_PID(token) == PID_CODE_IN)) {
status = -EPROTO; /* CERR nonzero + halt --> stall */
} elseif (QTD_CERR(token)) {
status = -EPIPE;
/* In theory, more than one of the following bits can be set *sincetheyarestickyandthetransactionisretried. *Whichtotestfirstisratherarbitrary.
*/
} elseif (token & QTD_STS_MMF) { /* fs/ls interrupt xfer missed the complete-split */
status = -EPROTO;
} elseif (token & QTD_STS_DBE) {
status = (QTD_PID(token) == PID_CODE_IN) /* IN ? */
? -ENOSR /* hc couldn't read data */
: -ECOMM; /* hc couldn't write data */
} elseif (token & QTD_STS_XACT) { /* timeout, bad CRC, wrong PID, etc */
ehci_dbg(ehci, "devpath %s ep%d%s 3strikes\n",
urb->dev->devpath,
usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) ? "in" : "out");
status = -EPROTO;
} else { /* unknown */
status = -EPROTO;
}
}
if (unlikely(urb->unlinked)) {
INCR(ehci->stats.unlink);
} else { /* report non-error and short read status as zero */ if (status == -EINPROGRESS || status == -EREMOTEIO)
status = 0;
INCR(ehci->stats.complete);
}
/* clean up any state from previous QTD ...*/ if (last) { if (likely (last->urb != urb)) {
ehci_urb_done(ehci, last->urb, last_status);
last_status = -EINPROGRESS;
}
ehci_qtd_free (ehci, last);
last = NULL;
}
/* ignore urbs submitted during completions we reported */ if (qtd == end) break;
/* hardware copies qtd out of qh overlay */
rmb ();
token = hc32_to_cpu(ehci, qtd->hw_token);
/* always clean up qtds the hc de-activated */
retry_xacterr: if ((token & QTD_STS_ACTIVE) == 0) {
/* Report Data Buffer Error: non-fatal but useful */ if (token & QTD_STS_DBE)
ehci_dbg(ehci, "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n",
urb,
usb_endpoint_num(&urb->ep->desc),
usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out",
urb->transfer_buffer_length,
qtd,
qh);
/* on STALL, error, and short reads this urb must *completeandallitsqtdsmustberecycled.
*/ if ((token & QTD_STS_HALT) != 0) {
/* retry transaction errors until we *reachthesoftwarexacterrlimit
*/ if ((token & QTD_STS_XACT) &&
QTD_CERR(token) == 0 &&
++qh->xacterrs < QH_XACTERR_MAX &&
!urb->unlinked) {
ehci_dbg(ehci, "detected XactErr len %zu/%zu retry %d\n",
qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs);
/* reset the token in the qtd and the *qhoverlay(whichstillcontains *theqtd)sothatwepickupfrom *whereweleftoff
*/
token &= ~QTD_STS_HALT;
token |= QTD_STS_ACTIVE |
(EHCI_TUNE_CERR << 10);
qtd->hw_token = cpu_to_hc32(ehci,
token);
wmb();
hw->hw_token = cpu_to_hc32(ehci,
token); goto retry_xacterr;
}
stopped = 1;
qh->unlink_reason |= QH_UNLINK_HALTED;
/* stop scanning when we reach qtds the hc is using */
} elseif (likely (!stopped
&& ehci->rh_state >= EHCI_RH_RUNNING)) { break;
/* scan the whole queue for unlinks whenever it stops */
} else {
stopped = 1;
/* cancel everything if we halt, suspend, etc */ if (ehci->rh_state < EHCI_RH_RUNNING) {
last_status = -ESHUTDOWN;
qh->unlink_reason |= QH_UNLINK_SHUTDOWN;
}
/* this qtd is active; skip it unless a previous qtd *foritsurbfaulted,oritsurbwascanceled.
*/ elseif (last_status == -EINPROGRESS && !urb->unlinked) continue;
/* An unlink may leave an incomplete *asynctransactionintheTTbuffer. *Wehavetoclearit.
*/
ehci_clear_tt_buffer(ehci, qh, urb, token);
}
}
/* unless we already know the urb's status, collect qtd status *andupdatecountofbytestransferred.incommonshortread *caseswithonlyonedataqtd(includingcontroltransfers), *queueprocessingwon'thalt.butwithtwoormoreqtds(for *example,witha32KBtransfer),whenthefirstqtdgetsa *shortreadthesecondmustberemovedbyhand.
*/ if (last_status == -EINPROGRESS) {
last_status = qtd_copy_status(ehci, urb,
qtd->length, token); if (last_status == -EREMOTEIO
&& (qtd->hw_alt_next
& EHCI_LIST_END(ehci)))
last_status = -EINPROGRESS;
/* As part of low/full-speed endpoint-halt processing *wemustcleartheTTbuffer(11.17.5).
*/ if (unlikely(last_status != -EINPROGRESS &&
last_status != -EREMOTEIO)) { /* The TT's in some hubs malfunction when they *receivethisrequestfollowingaSTALL(they *stopsendingisochronouspackets).Sincea *STALLcan'tleavetheTTbufferinabusy *state(ifyoubelieveFigures11-48-11-51 *intheUSB2.0spec),wewon'tcleartheTT *bufferinthiscase.Strictlyspeakingthis *isaviolationofthespec.
*/ if (last_status != -EPIPE)
ehci_clear_tt_buffer(ehci, qh, urb,
token);
}
}
/* if we're removing something not at the queue head, *patchthehardwarequeuepointer.
*/ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
last = list_entry (qtd->qtd_list.prev, struct ehci_qtd, qtd_list);
last->hw_next = qtd->hw_next;
}
/* remove qtd; it's recycled after possible urb completion */
list_del (&qtd->qtd_list);
last = qtd;
/* reinit the xacterr counter for the next qtd */
qh->xacterrs = 0;
}
/* last urb's completion might still need calling */ if (likely (last != NULL)) {
ehci_urb_done(ehci, last->urb, last_status);
ehci_qtd_free (ehci, last);
}
/* Do we need to rescan for URBs dequeued during a giveback? */ if (unlikely(qh->dequeue_during_giveback)) { /* If the QH is already unlinked, do the rescan now. */ if (state == QH_STATE_IDLE) goto rescan;
/* Otherwise the caller must unlink the QH. */
}
/* restore original state; caller must unlink or relink */
qh->qh_state = state;
/* be sure the hardware's done with the qh before refreshing *itafterfaultcleanup,orrecoveringfromsiliconwrongly *overlayingthedummyqtd(whichreducesDMAchatter). * *Wewon'trefreshaQHthat'slinked(aftertheHC *stoppedthequeue).Thatavoidsarace: *-HCreadsfirstpartofQH; *-CPUupdatesthatfirstpartandthetoken; *-HCreadsrestofthatQH,includingtoken *Result:HCgetsaninconsistentimage,andthen *DMAsto/fromthewrongmemory(corruptingit). * *Thatshouldberareforinterrupttransfers, *exceptmaybehighbandwidth...
*/ if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci))
qh->unlink_reason |= QH_UNLINK_DUMMY_OVERLAY;
/* Let the caller know if the QH needs to be unlinked. */ return qh->unlink_reason;
}
// Would be best to create all qh's from config descriptors, // when each interface/altsetting is established. Unlink // any previous qh and cancel its urbs first; endpoints are // implicitly reset then (data toggle too). // That'd mean updating how usbcore talks to HCDs. (2.7?)
/* support for tt scheduling, and access to toggles */
qh->ps.udev = urb->dev;
qh->ps.ep = urb->ep;
/* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW:
info1 |= QH_LOW_SPEED;
fallthrough;
case USB_SPEED_FULL: /* EPS 0 means "full" */ if (type != PIPE_INTERRUPT)
info1 |= (EHCI_TUNE_RL_TT << 28); if (type == PIPE_CONTROL) {
info1 |= QH_CONTROL_EP; /* for TT */
info1 |= QH_TOGGLE_CTL; /* toggle from qtd */
}
info1 |= maxp << 16;
info2 |= (EHCI_TUNE_MULT_TT << 30);
/* Some Freescale processors have an erratum in which the *portnumberinthequeueheadwas0..N-1insteadof1..N.
*/ if (ehci_has_fsl_portno_bug(ehci))
info2 |= (urb->dev->ttport-1) << 23; else
info2 |= urb->dev->ttport << 23;
/* set the address of the TT; for TDI's integrated *roothubtt,leaveitzeroed.
*/ if (tt && tt->hub != ehci_to_hcd(ehci)->self.root_hub)
info2 |= tt->hub->devnum << 16;
/* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
staticvoid enable_async(struct ehci_hcd *ehci)
{ if (ehci->async_count++) return;
/* Stop waiting to turn off the async schedule */
ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC);
/* Don't start the schedule until ASS is 0 */
ehci_poll_ASS(ehci);
turn_on_io_watchdog(ehci);
}
staticvoid disable_async(struct ehci_hcd *ehci)
{ if (--ehci->async_count) return;
/* The async schedule and unlink lists are supposed to be empty */
WARN_ON(ehci->async->qh_next.qh || !list_empty(&ehci->async_unlink) ||
!list_empty(&ehci->async_idle));
/* Don't turn off the schedule until ASS is 1 */
ehci_poll_ASS(ehci);
}
/* control qh may need patching ... */ if (unlikely (epnum == 0)) {
/* usb_reset_device() briefly reverts to address 0 */ if (usb_pipedevice (urb->pipe) == 0)
qh->hw->hw_info1 &= ~qh_addr_mask;
}
/* just one way to queue requests: swap with the dummy qtd. *onlyhcorqh_refresh()evermodifytheoverlay.
*/ if (likely (qtd != NULL)) { struct ehci_qtd *dummy;
dma_addr_t dma;
__hc32 token;
/* to avoid racing the HC, use the dummy td instead of *thefirsttdofourlist(becomesnewdummy).both *tdsstaydeactivateduntilwe'redone,whenthe *HCisallowedtofetchtheolddummy(4.10.2).
*/
token = qtd->hw_token;
qtd->hw_token = HALT_BIT(ehci);
/* hc must see the new dummy at list end */
dma = qtd->qtd_dma;
qtd = list_entry (qh->qtd_list.prev, struct ehci_qtd, qtd_list);
qtd->hw_next = QTD_NEXT(ehci, dma);
/* let the hc process these next qtds */
wmb ();
dummy->hw_token = token;
/* URBs map to sequences of QTDs: one logical transaction */
qtd = ehci_qtd_alloc(ehci, GFP_KERNEL); if (unlikely(!qtd)) return -1;
list_add_tail(&qtd->qtd_list, head);
qtd->urb = urb;
len = urb->transfer_buffer_length; /* *CheckiftherequestistoperformjusttheSETUPstage(getDesc) *asinSINGLE_STEP_SET_FEATUREtest,DATAstage(IN)happens *15secsafterthesetup
*/ if (is_setup) { /* SETUP pid, and interrupt after SETUP completion */
qtd_fill(ehci, qtd, urb->setup_dma, sizeof(struct usb_ctrlrequest),
QTD_IOC | token | (PID_CODE_SETUP << 8), 8);
submit_async(ehci, urb, &qtd_list, GFP_ATOMIC); return0; /*Return now; we shall come back after 15 seconds*/
}
/* *IN:datatransferstage:buffersetup:starttheINtxnphasefor *theget_DescSETUPwhichwassent15secondsback
*/
token ^= QTD_TOGGLE; /*We need to start IN with DATA-1 Pid-sequence*/
buf = urb->transfer_dma;
/* Add to the end of the list of QHs waiting for the next IAAD */
qh->qh_state = QH_STATE_UNLINK_WAIT;
list_add_tail(&qh->unlink_node, &ehci->async_unlink);
/* Unlink it from the schedule */
prev = ehci->async; while (prev->qh_next.qh != qh)
prev = prev->qh_next.qh;
staticvoid start_iaa_cycle(struct ehci_hcd *ehci)
{ /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
end_unlink_async(ehci);
/* Otherwise start a new IAA cycle if one isn't already running */
} elseif (ehci->rh_state == EHCI_RH_RUNNING &&
!ehci->iaa_in_progress) {
/* Make sure the unlinks are all visible to the hardware */
wmb();
/* If the controller isn't running, process all the waiting QHs */ if (ehci->rh_state < EHCI_RH_RUNNING)
list_splice_tail_init(&ehci->async_unlink, &ehci->async_idle);
/* Some reasons for unlinking guarantee the QH can't be active */ elseif (qh->unlink_reason & (QH_UNLINK_HALTED |
QH_UNLINK_SHORT_READ | QH_UNLINK_DUMMY_OVERLAY)) goto DelayDone;
/* The QH can't be active if the queue was and still is empty... */ elseif ((qh->unlink_reason & QH_UNLINK_QUEUE_EMPTY) &&
list_empty(&qh->qtd_list)) goto DelayDone;
/* ... or if the QH has halted */ elseif (qh->hw->hw_token & cpu_to_hc32(ehci, QTD_STS_HALT)) goto DelayDone;
/* Otherwise we have to wait until the QH stops changing */ else {
__hc32 qh_current, qh_token;
/* Find the last async QH which has been empty for a timer cycle */ for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) { if (list_empty(&qh->qtd_list) &&
qh->qh_state == QH_STATE_LINKED) {
++count; if (qh->unlink_cycle != ehci->async_unlink_cycle)
qh_to_unlink = qh;
}
}
/* If nothing else is being unlinked, unlink the last empty QH */ if (list_empty(&ehci->async_unlink) && qh_to_unlink) {
qh_to_unlink->unlink_reason |= QH_UNLINK_QUEUE_EMPTY;
start_unlink_async(ehci, qh_to_unlink);
--count;
}
/* Other QHs will be handled later */ if (count > 0) {
ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
++ehci->async_unlink_cycle;
}
}
#ifdef CONFIG_PM
/* The root hub is suspended; unlink all the async QHs */ staticvoid unlink_empty_async_suspended(struct ehci_hcd *ehci)
{ struct ehci_qh *qh;
/* makes sure the async qh will become idle */ /* caller must own ehci->lock */
staticvoid start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
{ /* If the QH isn't linked then there's nothing we can do. */ if (qh->qh_state != QH_STATE_LINKED) return;
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.27Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.