/* every endpoint has a ed, locate and maybe (re)initialize it */
ed = ed_get(ohci, urb->ep, urb->dev, pipe, urb->interval); if (! ed) return -ENOMEM;
/* for the private part of the URB we need the number of TDs (size) */ switch (ed->type) { case PIPE_CONTROL: /* td_submit_urb() doesn't yet handle these */ if (urb->transfer_buffer_length > 4096) return -EMSGSIZE;
/* 1 TD for setup, 1 for ACK, plus ... */
size = 2;
fallthrough; // case PIPE_INTERRUPT: // case PIPE_BULK: default:
size += number_of_tds(urb); /* maybe a zero-length packet to wrap it up */ if (size == 0)
size++; elseif ((urb->transfer_flags & URB_ZERO_PACKET) != 0
&& (urb->transfer_buffer_length
% usb_maxpacket(urb->dev, pipe)) == 0)
size++; break; case PIPE_ISOCHRONOUS: /* number of packets from URB */
size = urb->number_of_packets; break;
}
/* allocate the private part of the URB */
urb_priv = kzalloc(struct_size(urb_priv, td, size), mem_flags); if (!urb_priv) return -ENOMEM;
INIT_LIST_HEAD (&urb_priv->pending);
urb_priv->length = size;
urb_priv->ed = ed;
/* allocate the TDs (deferring hash chain updates) */ for (i = 0; i < size; i++) {
urb_priv->td [i] = td_alloc (ohci, mem_flags); if (!urb_priv->td [i]) {
urb_priv->length = i;
urb_free_priv (ohci, urb_priv); return -ENOMEM;
}
}
spin_lock_irqsave (&ohci->lock, flags);
/* don't submit to a dead HC */ if (!HCD_HW_ACCESSIBLE(hcd)) {
retval = -ENODEV; goto fail;
} if (ohci->rh_state != OHCI_RH_RUNNING) {
retval = -ENODEV; goto fail;
}
retval = usb_hcd_link_urb_to_ep(hcd, urb); if (retval) goto fail;
/* schedule the ed if needed */ if (ed->state == ED_IDLE) {
retval = ed_schedule (ohci, ed); if (retval < 0) {
usb_hcd_unlink_urb_from_ep(hcd, urb); goto fail;
}
/* Start up the I/O watchdog timer, if it's not running */ if (ohci->prev_frame_no == IO_WATCHDOG_OFF &&
list_empty(&ohci->eds_in_use) &&
!(ohci->flags & OHCI_QUIRK_QEMU)) {
ohci->prev_frame_no = ohci_frame_no(ohci);
mod_timer(&ohci->io_watchdog,
jiffies + IO_WATCHDOG_DELAY);
}
list_add(&ed->in_use_list, &ohci->eds_in_use);
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = ohci_frame_no(ohci);
/* Behind the scheduling threshold? */ if (unlikely(tick_before(frame, next))) {
/* URB_ISO_ASAP: Round up to the first available slot */ if (urb->transfer_flags & URB_ISO_ASAP) {
frame += (next - frame + ed->interval - 1) &
-ed->interval;
/* fill the TDs and link them to the ed; and *enablethatpartoftheschedule,ifneeded *andupdatecountofqueuedperiodicurbs
*/
urb->hcpriv = urb_priv;
td_submit_urb (ohci, urb);
/* Unless an IRQ completed the unlink while it was being *handedtous,flagitforunlinkandgiveback,andforce *someupcomingINTR_SFtocallfinish_unlinks()
*/
urb_priv = urb->hcpriv; if (urb_priv->ed->state == ED_OPER)
start_ed_unlink(ohci, urb_priv->ed);
if (ohci->rh_state != OHCI_RH_RUNNING) { /* With HC dead, we can clean up right away */
ohci_work(ohci);
}
}
spin_unlock_irqrestore (&ohci->lock, flags); return rc;
}
/* Software reset, after which the controller goes into SUSPEND */
ohci_writel(ohci, OHCI_HCR, &ohci->regs->cmdstatus);
ohci_readl(ohci, &ohci->regs->cmdstatus); /* flush the writes */
udelay(10);
/* REVISIT this BIOS handshake is now moved into PCI "quirks", and *wasneverneededformostnon-PCIsystems...removethecode?
*/
#ifndef IR_DISABLE /* SMM owns the HC? not for long! */ if (!no_handshake && ohci_readl (ohci,
&ohci->regs->control) & OHCI_CTRL_IR) {
u32 temp;
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
/* this timeout is arbitrary. we make it long, so systems *dependingonusbkeyboardsmaybeusableevenifthe *BIOS/SMMcodeseemsprettybroken.
*/
temp = 500; /* arbitrary: five seconds */
/* 2msec timelimit here means no irqs/preempt */
spin_lock_irq (&ohci->lock);
retry: /* HC Reset requires max 10 us delay */
ohci_writel (ohci, OHCI_HCR, &ohci->regs->cmdstatus);
val = 30; /* ... allow extra time */ while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) { if (--val == 0) {
spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "USB HC reset timed out!\n"); return -1;
}
udelay (1);
}
/* now we're in the SUSPEND state ... must go OPERATIONAL *within2msecelseHCentersRESUME * *...butsomehardwarewon'tinitfmInterval"bythebook" *(SiS,OPTi...),soresetagaininstead.SiSdoesn'tneed *thisifwewritefmIntervalafterwe'reOPERATIONAL. *UnclearaboutALi,ServerWorks,andothers...thiscould *easilybealongstandingbuginchipinitonLinux.
*/ if (ohci->flags & OHCI_QUIRK_INITRESET) {
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); // flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
}
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
/* a reset clears this */
ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
periodic_reinit (ohci);
/* some OHCI implementations are finicky about how they init. *bogusvaluesheremeannotevenenumerationcouldwork.
*/ if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0
|| !ohci_readl (ohci, &ohci->regs->periodicstart)) { if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
ohci->flags |= OHCI_QUIRK_INITRESET;
ohci_dbg (ohci, "enabling initreset quirk\n"); goto retry;
}
spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "init err (%08x %04x)\n",
ohci_readl (ohci, &ohci->regs->fminterval),
ohci_readl (ohci, &ohci->regs->periodicstart)); return -EOVERFLOW;
}
/* use rhsc irqs after hub_wq is allocated */
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
hcd->uses_new_polling = 1;
/* wake on ConnectStatusChange, matching external hubs */
ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_INIT;
ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
ohci_writel (ohci, mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
val = roothub_a (ohci); /* Configure for per-port over-current protection by default */
val &= ~RH_A_NOCP;
val |= RH_A_OCPM; if (ohci->flags & OHCI_QUIRK_SUPERIO) { /* NSC 87560 and maybe others. *Gangedpowerswitching,noover-currentprotection.
*/
val |= RH_A_NOCP;
val &= ~(RH_A_POTPGT | RH_A_NPS | RH_A_PSM | RH_A_OCPM);
} elseif ((ohci->flags & OHCI_QUIRK_AMD756) ||
(ohci->flags & OHCI_QUIRK_HUB_POWER)) { /* hub power always on; required for AMD-756 and some *Macplatforms.
*/
val |= RH_A_NPS;
}
ohci_writel(ohci, val, &ohci->regs->roothub.a);
/* ohci_start routine for generic controller start of all OHCI bus glue */ staticint ohci_start(struct usb_hcd *hcd)
{ struct ohci_hcd *ohci = hcd_to_ohci(hcd); int ret;
ret = ohci_run(ohci); if (ret < 0) {
ohci_err(ohci, "can't start\n");
ohci_stop(hcd);
} return ret;
}
/* *OnewaytolosetrackofcompletedTDsisifthecontroller *neverwritesbackthedonequeuehead.Ifithasn'tbeen *writtenbacksincethelasttimethisfunctionranandifit *wasnon-emptyatthattime,somethingisbadlywrongwiththe *hardware.
*/
status = ohci_readl(ohci, &ohci->regs->intrstatus); if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { if (ohci->prev_donehead) {
ohci_err(ohci, "HcDoneHead not written back; disabled\n");
died:
usb_hc_died(ohci_to_hcd(ohci));
ohci_dump(ohci);
_ohci_shutdown(ohci_to_hcd(ohci)); goto done;
} else { /* No write back because the done queue was empty */
takeback_all_pending = true;
}
}
/* Check every ED which might have pending TDs */
list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) { if (ed->pending_td) { if (takeback_all_pending ||
OKAY_TO_TAKEBACK(ohci, ed)) { unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO);
ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n", 0x007f & tmp,
(0x000f & (tmp >> 7)) +
((tmp & ED_IN) >> 5));
add_to_done_list(ohci, ed->pending_td);
}
}
/* Starting from the latest pending TD, */
td = ed->pending_td;
/* or the last TD on the done list, */ if (!td) {
list_for_each_entry(td_next, &ed->td_list, td_list) { if (!td_next->next_dl_td) break;
td = td_next;
}
}
/* find the last TD processed by the controller. */
head = hc32_to_cpu(ohci, READ_ONCE(ed->hwHeadP)) & TD_MASK;
td_start = td;
td_next = list_prepare_entry(td, &ed->td_list, td_list);
list_for_each_entry_continue(td_next, &ed->td_list, td_list) { if (head == (u32) td_next->td_dma) break;
td = td_next; /* head pointer has passed this TD */
} if (td != td_start) { /* *IncaseaWDHcycleisinprogress,wewillwait *forthenexttwocyclestocompletebeforeassuming *thisTDwillnevergetonthedonequeue.
*/
ed->takeback_wdh_cnt = ohci->wdh_cnt + 2;
ed->pending_td = td;
}
}
ohci_work(ohci);
if (ohci->rh_state == OHCI_RH_RUNNING) {
/* *Sometimesacontrollerjuststopsworking.Wecantell *bycheckingthattheframecounterhasadvancedsince *thelasttimeweran. * *Butbecareful:Somecontrollersviolatethespecby *stoppingtheirframecounterwhennoportsareactive.
*/
frame_no = ohci_frame_no(ohci); if (frame_no == ohci->prev_frame_no) { int active_cnt = 0; int i; unsigned tmp;
for (i = 0; i < ohci->num_ports; ++i) {
tmp = roothub_portstatus(ohci, i); /* Enabled and not suspended? */ if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS))
++active_cnt;
}
/* Read interrupt status (and flush pending writes). We ignore the *optimizationofcheckingtheLSBofhcca->done_head;itdoesn't *workonallsystems(edgetriggeringforOHCIcanbeafactor).
*/
ints = ohci_readl(ohci, ®s->intrstatus);
/* Check for an all 1's result which is a typical consequence *ofdead,unclocked,orunplugged(CardBus...)devices
*/
again: if (ints == ~(u32)0) {
ohci->rh_state = OHCI_RH_HALTED;
ohci_dbg (ohci, "device removed!\n");
usb_hc_died(hcd); return IRQ_HANDLED;
}
/* We only care about interrupts that are enabled */
ints &= ohci_readl(ohci, ®s->intrenable);
/* interrupt for some other device? */ if (ints == 0 || unlikely(ohci->rh_state == OHCI_RH_HALTED)) return IRQ_NOTMINE;
if (ints & OHCI_INTR_UE) { // e.g. due to PCI Master/Target Abort if (quirk_nec(ohci)) { /* Workaround for a silicon bug in some NEC chips used *inApple'sPowerBooks.AdaptedfromDarwincode.
*/
ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n");
int ohci_resume(struct usb_hcd *hcd, bool hibernated)
{ struct ohci_hcd *ohci = hcd_to_ohci(hcd); int port; bool need_reinit = false;
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/* Make sure resume from hibernation re-enumerates everything */ if (hibernated)
ohci_usb_reset(ohci);
/* See if the controller is already running or has been reset */
ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
need_reinit = true;
} else { switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_OPER: case OHCI_USB_RESET:
need_reinit = true;
}
}
/* If needed, reinitialize and suspend the root hub */ if (need_reinit) {
spin_lock_irq(&ohci->lock);
ohci_rh_resume(ohci);
ohci_rh_suspend(ohci, 0);
spin_unlock_irq(&ohci->lock);
}
/* Normally just turn on port power and enable interrupts */ else {
ohci_dbg(ohci, "powerup ports\n"); for (port = 0; port < ohci->num_ports; port++)
ohci_writel(ohci, RH_PS_PPS,
&ohci->regs->roothub.portstatus[port]);
void ohci_init_driver(struct hc_driver *drv, conststruct ohci_driver_overrides *over)
{ /* Copy the generic table to drv and then apply the overrides */
*drv = ohci_hc_driver;
if (over) {
drv->product_desc = over->product_desc;
drv->hcd_priv_size += over->extra_priv_size; if (over->reset)
drv->reset = over->reset;
}
}
EXPORT_SYMBOL_GPL(ohci_init_driver);
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.