21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/Spinlock.h"
24#include "pedigree/kernel/machine/Device.h"
25#include "pedigree/kernel/machine/IrqManager.h"
26#include "pedigree/kernel/machine/Machine.h"
27#include "pedigree/kernel/machine/Pci.h"
28#include "pedigree/kernel/machine/PciFunctionState.h"
29#include "pedigree/kernel/machine/types.h"
30#include "pedigree/kernel/panic.h"
31#include "pedigree/kernel/process/Mutex.h"
32#include "pedigree/kernel/process/TerminationDeferral.h"
33#include "pedigree/kernel/process/Thread.h"
34#include "pedigree/kernel/processor/InterruptHandler.h"
35#include "pedigree/kernel/processor/InterruptManager.h"
36#include "pedigree/kernel/processor/IoBase.h"
37#include "pedigree/kernel/processor/MemoryRegion.h"
38#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
39#include "pedigree/kernel/processor/Processor.h"
40#include "pedigree/kernel/processor/ProcessorInformation.h"
41#include "pedigree/kernel/processor/VirtualAddressSpace.h"
42#include "pedigree/kernel/processor/state_forward.h"
43#include "pedigree/kernel/processor/types.h"
44#include "pedigree/kernel/time/Time.h"
45#include "pedigree/kernel/utilities/ExtensibleBitmap.h"
46#include "pedigree/kernel/utilities/RequestQueue.h"
47#include "pedigree/kernel/utilities/String.h"
48#include "pedigree/kernel/utilities/Vector.h"
49#include "pedigree/kernel/utilities/utility.h"
51#include "EhciLegacy.h"
52#include "modules/drivers/common/DmaBuffer.h"
53#include "modules/system/usb/Usb.h"
54#include "modules/system/usb/UsbHub.h"
57constexpr size_t EhciHardwarePageBytes = 4096;
58constexpr size_t EhciHardwarePageShift = 12;
59constexpr size_t EhciDescriptorOffsetMask = EhciHardwarePageBytes - 1;
60constexpr size_t EhciQhRegionBytes = 2 * EhciHardwarePageBytes;
61constexpr size_t EhciFrameListOffset = EhciQhRegionBytes;
62constexpr size_t EhciQtdListOffset = EhciFrameListOffset + EhciHardwarePageBytes;
63constexpr size_t EhciDmaRegionBytes = EhciQtdListOffset + EhciHardwarePageBytes;
64constexpr size_t EhciQtdBufferPageCount = 5;
65constexpr size_t EhciQtdBufferWindowBytes = EhciQtdBufferPageCount * EhciHardwarePageBytes;
66constexpr physical_uintptr_t EhciHighestDmaAddress = 0xffffffff;
67static_assert((
size_t{1} << EhciHardwarePageShift) == EhciHardwarePageBytes);
69struct EhciDmaTransfer {
76EhciDmaTransfer* dmaTransfer(
const Ehci::qTD* qtd) {
77 return reinterpret_cast<EhciDmaTransfer*
>(
static_cast<uintptr_t
>(qtd->pDma));
81 auto* transfer = dmaTransfer(qtd);
84 delete transfer->region;
89void copyDmaInput(
const Ehci::qTD* qtd, ssize_t bytes) {
90 auto* transfer = dmaTransfer(qtd);
91 if (!transfer || !transfer->input || bytes <= 0)
93 size_t count =
static_cast<size_t>(bytes);
94 if (count > transfer->bytes)
95 count = transfer->bytes;
96 MemoryCopy(
reinterpret_cast<void*
>(transfer->client), transfer->region->virtualAddress(), count);
100#define INDEX_FROM_QTD(ptr) \
101 (((reinterpret_cast<uintptr_t>((ptr)) & EhciDescriptorOffsetMask) / sizeof(qTD)))
102#define PHYS_QTD(idx) (m_pqTDListPhys + ((idx) * sizeof(qTD)))
104static int threadStub(
void* p);
106static constexpr size_t EhciPollLimit = 100;
107static constexpr uint32_t EhciAsyncScheduleStatus = 0x8000;
108static constexpr uint32_t EhciScheduleStatusMask = 0xc000;
110static bool waitForMmioState(
IoBase* base,
size_t registerOffset, uint32_t mask, uint32_t expected,
111 size_t pollLimit = EhciPollLimit) {
113 const auto deadline = Time::getTicks() + pollLimit * Time::Multiplier::Millisecond;
115 if ((base->
read32(registerOffset) & mask) == expected) {
120 }
while (Time::getTicks() < deadline);
121 return (base->
read32(registerOffset) & mask) == expected;
130 m_IrqProcessingLock(),
131 m_CompletionDeliveries(),
134 m_pFrameList(nullptr),
138 m_pCurrentQueueTail(0),
139 m_pCurrentQueueHead(0),
142 m_DequeueStopping(false),
144 m_SubmissionOperations(),
145 m_CancelOperations(),
146 m_CallbackOperations(),
148 m_InterruptHandlerRegistered(false),
149 m_InterruptClosure(0) {
150 setSpecificType(
String(
"EHCI"));
153bool Ehci::initialiseController() {
156 ERROR(
"USB: EHCI: target pages cannot represent 4 KiB EHCI buffer pages");
162 m_EhciMR, DriverDma::pageCountForBytes(EhciDmaRegionBytes),
165 ERROR(
"USB: EHCI: Couldn't allocate Memory Region!");
169 uintptr_t virtualBase =
reinterpret_cast<uintptr_t
>(m_EhciMR.
virtualAddress());
171 m_pQHList =
reinterpret_cast<QH*
>(virtualBase);
172 m_pFrameList =
reinterpret_cast<uint32_t*
>(virtualBase + EhciFrameListOffset);
173 m_pqTDList =
reinterpret_cast<qTD*
>(virtualBase + EhciQtdListOffset);
174 m_pQHListPhys = physicalBase;
175 m_pFrameListPhys = physicalBase + EhciFrameListOffset;
176 m_pqTDListPhys = physicalBase + EhciQtdListOffset;
178 DoubleWordSet(m_pFrameList, 1, 0x400);
181 auto& pci = PciBus::instance();
183 if (!pci.inspectFunction(
this, pciState)) {
184 ERROR(
"EHCI: unsupported inherited PCI function state");
187 m_FirmwarePciCommand = pciState.command;
188 const uint32_t bar = pciState.bars[0];
189 if ((bar & 1U) || ((bar & 6U) != 0 && (bar & 6U) != 4))
191 uint64_t base = bar & ~15U;
193 base |= uint64_t{pciState.bars[1]} << 32;
196 if (address->m_Name ==
"bar0" && base && !address->m_IsIoSpace && address->m_Address == base)
198 if (!mapping || mapping->
m_Size < 0x20 || !pci.updateCommand(
this, 0, 2))
202 m_pBase = mapping->
m_Io;
209 uint32_t hccapbase = m_pBase->
read32(EHCI_CAPLENGTH);
210 uint16_t version = hccapbase >> 16;
212 m_nOpRegsOffset = hccapbase & 0xFF;
214 NOTICE(
"EHCI operation registers are at offset " << m_nOpRegsOffset);
216 if (m_nOpRegsOffset < 0x10 || m_nOpRegsOffset > m_pBase->
size() ||
217 m_pBase->
size() - m_nOpRegsOffset < EHCI_PORTSC +
sizeof(uint32_t)) {
223 NOTICE(
"EHCI controller version " << (version >> 8) <<
"." << (version & 0xFF));
227 uint32_t hcsparams = m_pBase->
read32(EHCI_HCSPARAMS);
228 m_nPorts = hcsparams & 0xF;
229 if (!UsbHcd::validEhciRootPortCount(m_nPorts)) {
230 ERROR(
"EHCI: unsupported root-port count " <<
Dec << m_nPorts <<
Hex);
234 NOTICE(
"EHCI controller has " <<
Dec << m_nPorts <<
Hex <<
" physical ports.");
237 uint32_t hccparams = m_pBase->
read32(EHCI_HCCPARAMS);
238 uint8_t eecp = (hccparams >> 8) & 0xFF;
239 constexpr uint8_t EecpMinimum = 0x40;
240 constexpr uint8_t EecpMaximum = 0xfc;
241 if (eecp && (eecp < EecpMinimum || eecp > EecpMaximum || (eecp & 0x3))) {
242 ERROR(
"EHCI: EECP pointer is invalid");
247 DEBUG_LOG(
"EHCI: Host controller " << (hccparams & 1 ?
"does" :
"does not")
248 <<
" require 64-bit data structures.");
249 DEBUG_LOG(
" Host controller " << (hccparams & 2 ?
"does" :
"does not")
250 <<
" allow us to use frame lists with "
251 "anything other than 1024 items in them.");
252 DEBUG_LOG(
" Host controller " << (hccparams & 4 ?
"does" :
"does not")
253 <<
" support the asynchronous schedule park capability.");
254 DEBUG_LOG(
" HCCAPBASE is " << hccapbase);
255 DEBUG_LOG(
" HCCPARAMS is " << hccparams);
256 DEBUG_LOG(
" HCSPARAMS is " << hcsparams);
257 DEBUG_LOG(
" EECP is " << eecp);
262 constexpr size_t EecpSlotCount = (0x100 - EecpMinimum) / 4;
263 size_t capabilityBudget = EecpSlotCount;
264 uint64_t visitedCapabilities = 0;
265 bool foundLegacy =
false;
267 if (!capabilityBudget) {
268 ERROR(
"EHCI: EECP capability chain exceeded PCI config space");
272 const size_t capabilitySlot = (eecp - EecpMinimum) / 4;
273 const uint64_t capabilityBit = uint64_t(1) << capabilitySlot;
274 if (visitedCapabilities & capabilityBit) {
275 ERROR(
"EHCI: cyclic EECP capability chain");
278 visitedCapabilities |= capabilityBit;
281 DEBUG_LOG(
"EHCI: Reading LEGSUP register and checking for BIOS ownership.");
284 if (!pci.readConfig32(
this, eecp, legsup))
287 if ((legsup & 0xff) == 1) {
289 ERROR(
"EHCI: truncated USB legacy capability");
293 ERROR(
"EHCI: duplicate USB legacy capability");
297 if (!acquireFirmware(eecp))
301 eecp = (legsup >> 8) & 0xFF;
302 if (eecp && (eecp < EecpMinimum || eecp > EecpMaximum || (eecp & 0x3))) {
303 ERROR(
"EHCI: EECP capability chain contains an invalid pointer");
309 m_HardwareOwned =
true;
310 m_HardwareTouched =
true;
311 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_INTR);
312 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
315 DEBUG_LOG(
"USB: EHCI: disabling running schedules");
319 m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD) & ~(EHCI_CMD_ASYNCLE | EHCI_CMD_PERIODICLE),
320 m_nOpRegsOffset + EHCI_CMD);
321 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EhciScheduleStatusMask, 0)) {
322 ERROR(
"EHCI: schedules did not stop within 100 ms");
326 uint32_t status = m_pBase->
read32(m_nOpRegsOffset + EHCI_STS);
327 if (!(status & EHCI_STS_HALTED)) {
329 DEBUG_LOG(
"USB: EHCI: pausing controller");
332 m_pBase->
write32(m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD) & ~EHCI_CMD_RUN,
333 m_nOpRegsOffset + EHCI_CMD);
334 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EHCI_STS_HALTED, EHCI_STS_HALTED)) {
335 ERROR(
"EHCI: controller did not halt within 100 ms");
341 DEBUG_LOG(
"USB: EHCI: resetting controller");
344 m_pBase->
write32(EHCI_CMD_HCRES, m_nOpRegsOffset + EHCI_CMD);
345 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_CMD, EHCI_CMD_HCRES, 0)) {
346 ERROR(
"EHCI: controller reset did not complete within 100 ms");
350 DEBUG_LOG(
"USB: EHCI: Reset complete, status: " << m_pBase->
read32(m_nOpRegsOffset + EHCI_STS)
355 if (!pci.updateCommand(
this, 4, 2 | 0x400) || !pci.disableMessageInterrupts(
this, pciState)) {
356 ERROR(
"EHCI: could not establish masked PCI interrupt state");
366 ERROR(
"EHCI: request queue did not enter the accepting state");
370 for (
size_t i = 0; i < m_nPorts; ++i) {
371 if (!m_PortChanges[i].configure(*
this, 0, i)) {
372 ERROR(
"EHCI: could not configure root-port publication " << i);
380 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_INTR);
381 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
386 static_cast<IrqHandler*
>(
this),
this, IrqPolicy::pciIntxThreaded());
387 m_InterruptHandlerRegistered = m_IrqId != 0;
392 ERROR(
"EHCI requires synchronous IRQ handler lifetime management");
398 if (!m_InterruptHandlerRegistered) {
399 ERROR(
"EHCI: could not register interrupt handler");
405 Machine::instance().getIrqManager()->
control(
411 if (!pci.resourcesUnchanged(
this, pciState)) {
412 ERROR(
"EHCI: PCI resources changed during handoff");
418 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_CTRLDSEG);
422 m_pBase->
write32(m_pFrameListPhys, m_nOpRegsOffset + EHCI_PERIODICLP);
424 Time::delay(5 * Time::Multiplier::Millisecond);
429 QH* pDummyQH = &m_pQHList[0];
430 qTD* pDummyTD = &m_pqTDList[0];
431 ByteSet(pDummyQH, 0,
sizeof(QH));
432 ByteSet(pDummyTD, 0,
sizeof(qTD));
435 pDummyTD->bNextInvalid = pDummyTD->bAltNextInvalid = 1;
438 pDummyQH->pNext = m_pQHListPhys >> 5;
439 pDummyQH->nNextType = 1;
441 pDummyQH->pQTD = m_pqTDListPhys >> 5;
445 pDummyQH->pMetaData =
new QH::MetaData;
446 pDummyQH->pMetaData->pFirstQTD = 0;
447 pDummyQH->pMetaData->pLastQTD = pDummyTD;
448 pDummyQH->pMetaData->pNext = pDummyQH;
449 pDummyQH->pMetaData->pPrev = pDummyQH;
451 MemoryCopy(&pDummyQH->overlay, pDummyTD,
sizeof(qTD));
453 m_pCurrentQueueHead = m_pCurrentQueueTail = pDummyQH;
456 m_pBase->
write32(m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD) & ~EHCI_CMD_ASYNCLE,
457 m_nOpRegsOffset + EHCI_CMD);
458 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EhciAsyncScheduleStatus, 0)) {
459 ERROR(
"EHCI asynchronous schedule did not stop within 100 ms");
464 m_pBase->
write32(m_pQHListPhys, m_nOpRegsOffset + EHCI_ASYNCLP);
467 m_pBase->
write32((m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD) & ~0xFF0000) | 0x80000,
468 m_nOpRegsOffset + EHCI_CMD);
472 if (!pci.updateCommand(
this, 0, 6 | 0x400))
477 m_pBase->
write32(m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD) | EHCI_CMD_RUN,
478 m_nOpRegsOffset + EHCI_CMD);
479 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EHCI_STS_HALTED, 0)) {
480 ERROR(
"EHCI controller did not start within 100 ms");
485 threadStub,
reinterpret_cast<void*
>(
this)));
486 m_DequeueThread->setName(
"EHCI dequeue");
489 if (!pci.updateCommand(
this, 0x400, 6))
495 m_pBase->
write32(0x3b, m_nOpRegsOffset + EHCI_INTR);
496 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
499 m_pBase->
write32(1, m_nOpRegsOffset + EHCI_CFGFLAG);
504 m_pBase->
write32(m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD) | 0x900,
505 m_nOpRegsOffset + EHCI_CMD);
509 m_pBase->
write32(m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD) | EHCI_CMD_ASYNCLE,
510 m_nOpRegsOffset + EHCI_CMD);
511 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EhciAsyncScheduleStatus,
512 EhciAsyncScheduleStatus)) {
513 ERROR(
"EHCI asynchronous schedule did not start within 100 ms");
519 m_pBase->
write32(EHCI_STS_PORTCH, m_nOpRegsOffset + EHCI_STS);
520 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_STS);
525 m_Startup.enableRecovery(m_Handoff.wasBiosOwned());
526 m_InitialPortMask = (1U << m_nPorts) - 1;
527 if (m_InitialPortMask && !m_Startup.begin())
532 for (
size_t i = 0; i < m_nPorts; i++) {
534 DEBUG_LOG(
"USB: EHCI: Port " <<
Dec << i <<
Hex <<
" - status initially: "
535 << m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + i * 4));
538 if (!(m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + i * 4) & EHCI_PORTSC_PPOW)) {
539 modifyPortControl(m_nOpRegsOffset + EHCI_PORTSC + i * 4, 0, EHCI_PORTSC_PPOW);
540 Time::delay(20 * Time::Multiplier::Millisecond);
542 DEBUG_LOG(
"USB: EHCI: Port " <<
Dec << i <<
Hex <<
" - status after power-up: "
543 << m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + i * 4));
548 const size_t portRegister = m_nOpRegsOffset + EHCI_PORTSC + i * 4;
549 modifyPortControl(portRegister, EHCI_PORTSC_PRES, 0);
550 if (!waitForMmioState(m_pBase, portRegister, EHCI_PORTSC_PRES, 0)) {
551 ERROR(
"EHCI: initial reset on port " <<
Dec << i <<
Hex
552 <<
" did not clear within "
555 StartupActivity failedPort(
this);
559 completeInitialPort(i);
563 constexpr uint32_t ChangeMask = EHCI_PORTSC_CSCH | EHCI_PORTSC_ENCH | EHCI_PORTSC_OCCH;
564 const uint32_t portStatus = m_pBase->
read32(portRegister);
565 const uint32_t acknowledgeMask = portStatus & ChangeMask;
566 if (acknowledgeMask) {
567 m_pBase->
write32(UsbHcd::selectiveW1cValue(portStatus, ChangeMask, acknowledgeMask),
569 (void)m_pBase->
read32(portRegister);
575 const auto observation = m_PortChanges[i].observe();
576 if (!UsbHcd::PortChangeRequest::canAcknowledge(observation.result)) {
577 panic(
"EHCI could not publish its initial root-port state");
579 m_PortChanges[i].acknowledge(observation.generation);
586 m_pBase->
write32(0x3f, m_nOpRegsOffset + EHCI_INTR);
589 m_pBase->
write32(0x3b, m_nOpRegsOffset + EHCI_INTR);
596 m_RecoveryStopping =
true;
598 m_RecoveryThread.
join();
599 shutdownController();
603void Ehci::shutdownController() {
604 if (m_ControllerStopped)
606 m_ControllerStopped =
true;
616 if (m_HardwareOwned) {
617 const uint32_t interrupts = m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
618 m_pBase->
write32(interrupts & ~EHCI_STS_PORTCH, m_nOpRegsOffset + EHCI_INTR);
619 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
624 for (
size_t i = 0; i < m_nPorts; ++i) {
642 if (m_HardwareOwned) {
643 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_INTR);
644 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
645 const uint32_t pending = m_pBase->
read32(m_nOpRegsOffset + EHCI_STS) & 0x3f;
647 m_pBase->
write32(pending, m_nOpRegsOffset + EHCI_STS);
648 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_STS);
654 if (m_HardwareOwned) {
655 const uint32_t command = m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD);
656 m_pBase->
write32(command & ~EHCI_CMD_RUN, m_nOpRegsOffset + EHCI_CMD);
657 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD);
659 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EHCI_STS_HALTED,
661 panic(
"EHCI teardown could not establish the DMA halt boundary");
664 const uint32_t pending = m_pBase->
read32(m_nOpRegsOffset + EHCI_STS) & 0x3f;
666 m_pBase->
write32(pending, m_nOpRegsOffset + EHCI_STS);
667 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_STS);
669 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_ASYNCLP);
670 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_PERIODICLP);
671 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_PERIODICLP);
675 if (m_HardwareOwned && !PciBus::instance().updateCommand(
this, 4, 0x400))
676 panic(
"EHCI teardown could not disable PCI DMA and INTx");
677 if (m_InterruptHandlerRegistered) {
678 if (!Machine::instance().getIrqManager()->unregisterHandler(m_IrqId,
681 "EHCI teardown could not synchronously unregister its IRQ "
685 m_InterruptHandlerRegistered =
false;
689 m_DequeueStopping =
true;
691 m_DequeueThread.
join();
704 DoubleWordSet(m_pFrameList, 1, 0x400);
705 for (
size_t i = 0; i < 1024; ++i) {
706 if (m_FrameBitmap.
test(i))
707 m_FrameBitmap.
clear(i);
712 constexpr size_t QhCount = EhciQhRegionBytes /
sizeof(QH);
713 for (
size_t i = 1; i < QhCount; ++i) {
714 if (!m_QHBitmap.
test(i))
717 QH* qh = &m_pQHList[i];
718 if (!qh->pMetaData) {
719 ByteSet(qh, 0,
sizeof(QH));
724 if (qh->pMetaData->bPeriodic ||
725 qh->pMetaData->completion.state() == UsbHcd::TransferCompletion::State::Idle) {
731 if (qh->pMetaData->completion.claimForTeardown(-TransactionError, claim)) {
732 captureCompletionLocked(i, qh, claim, completions);
736 if (m_QHBitmap.
test(0)) {
737 QH* dummy = &m_pQHList[0];
739 dummy->pNext = m_pQHListPhys >> 5;
740 dummy->nNextType = 1;
741 if (dummy->pMetaData) {
742 dummy->pMetaData->pNext = dummy;
743 dummy->pMetaData->pPrev = dummy;
745 m_pCurrentQueueHead = m_pCurrentQueueTail = dummy;
749 if (completions.
count())
753 while (completions.
count()) {
754 auto* completion = completions.
popFront();
768 constexpr size_t QhCount = EhciQhRegionBytes /
sizeof(QH);
769 for (
size_t i = 1; i < QhCount; ++i)
770 assert(!m_QHBitmap.
test(i));
772 if (m_QHBitmap.
test(0)) {
773 delete m_pQHList[0].pMetaData;
774 ByteSet(&m_pQHList[0], 0,
sizeof(QH));
780 constexpr size_t QtdCount = EhciHardwarePageBytes /
sizeof(qTD);
781 for (
size_t i = 1; i < QtdCount; ++i)
782 assert(!m_qTDBitmap.
test(i));
783 if (m_qTDBitmap.
test(0)) {
784 ByteSet(&m_pqTDList[0], 0,
sizeof(qTD));
785 m_qTDBitmap.
clear(0);
789 for (
size_t i = 0; i < 1024; ++i)
790 assert(!m_FrameBitmap.
test(i));
791 m_pCurrentQueueHead =
nullptr;
792 m_pCurrentQueueTail =
nullptr;
794 m_HardwareOwned =
false;
797static int threadStub(
void* p) {
798 Ehci* pEhci =
reinterpret_cast<Ehci*
>(p);
804struct EhciCompletionCleanup {
811void Ehci::releaseQtdChainLocked(QH* qh) {
812 if (!qh || !qh->pMetaData || !qh->pMetaData->pFirstQTD)
815 constexpr size_t QtdCount = EhciHardwarePageBytes /
sizeof(qTD);
816 size_t qtdIndex = INDEX_FROM_QTD(qh->pMetaData->pFirstQTD);
817 size_t budget = QtdCount;
818 bool foundLast =
false;
821 if (qtdIndex >= QtdCount)
822 panic(
"EHCI: invalid qTD index during reclamation");
824 qTD* qtd = &m_pqTDList[qtdIndex];
825 const bool last = qtd->bNextInvalid;
826 size_t nextIndex = 0;
828 nextIndex = ((qtd->pNext << 5) & EhciDescriptorOffsetMask) /
sizeof(qTD);
830 if (m_qTDBitmap.
test(qtdIndex))
831 m_qTDBitmap.
clear(qtdIndex);
832 releaseDmaTransfer(qtd);
833 ByteSet(qtd, 0,
sizeof(qTD));
839 if (nextIndex == qtdIndex)
840 panic(
"EHCI: circular qTD chain during reclamation");
841 qtdIndex = nextIndex;
844 panic(
"EHCI: qTD reclamation exceeded its descriptor budget");
846 qh->pMetaData->pFirstQTD =
nullptr;
847 qh->pMetaData->pLastQTD =
nullptr;
849 ByteSet(&qh->overlay, 0,
sizeof(qTD));
852void Ehci::reclaimQhLocked(
size_t transaction) {
853 if (!m_pQHList || !m_QHBitmap.
test(transaction))
856 QH* qh = &m_pQHList[transaction];
858 releaseQtdChainLocked(qh);
859 delete qh->pMetaData;
861 ByteSet(qh, 0,
sizeof(QH));
862 m_QHBitmap.
clear(transaction);
865void Ehci::captureCompletionLocked(
size_t transaction, QH* qh,
868 assert(qh && qh->pMetaData && !qh->pMetaData->bPeriodic);
869 assert(claim.generation == qh->pMetaData->completion.generation());
871 releaseQtdChainLocked(qh);
872 qh->pMetaData->bIgnore =
true;
873 auto* cleanup =
new EhciCompletionCleanup{
this, transaction, claim.generation};
875 claim.callback, claim.parameter, claim.result,
876 finishDeferredCompletion, cleanup));
879void Ehci::finishDeferredCompletion(
void* context) {
880 auto* cleanup =
reinterpret_cast<EhciCompletionCleanup*
>(context);
881 Ehci* controller = cleanup->controller;
884 const size_t transaction = cleanup->transaction;
885 if (controller->m_QHBitmap.
test(transaction)) {
886 QH* qh = &controller->m_pQHList[transaction];
887 if (qh->pMetaData && qh->pMetaData->completion.generation() == cleanup->generation) {
888 controller->reclaimQhLocked(transaction);
895void Ehci::rebuildPeriodicScheduleLocked() {
896 constexpr size_t Count = EhciQhRegionBytes /
sizeof(QH);
897 for (
size_t frame = 0; frame < 1024; ++frame) {
898 m_pFrameList[frame] = 1;
899 QH* previous =
nullptr;
902 for (
size_t interval = 1024; interval; interval /= 2) {
903 if (frame % interval)
905 for (
size_t i = 1; i < Count; ++i) {
906 QH* qh = &m_pQHList[i];
907 if (!m_QHBitmap.
test(i) || !qh->pMetaData || !qh->pMetaData->pCallback ||
908 !qh->pMetaData->bPeriodic || qh->pMetaData->periodicInterval != interval)
910 const uint32_t address = m_pQHListPhys + i *
sizeof(QH);
912 previous->pNext = address >> 5;
913 previous->nNextType = 1;
914 previous->bNextInvalid = 0;
916 m_pFrameList[frame] = address | 2U;
917 qh->bNextInvalid = 1;
924void Ehci::rearmPeriodicCompletion(
void* context) {
925 auto* rearm =
static_cast<EhciCompletionCleanup*
>(context);
926 Ehci* controller = rearm->controller;
931 QH* qh = &controller->m_pQHList[rearm->transaction];
932 auto* metadata = qh->pMetaData;
933 if (!metadata || !metadata->pCallback || metadata->periodicGeneration != rearm->generation)
935 qTD next = metadata->periodicTemplate;
937 *metadata->pFirstQTD = next;
938 MemoryCopy(&qh->overlay, metadata->pFirstQTD,
sizeof(qTD));
939 metadata->periodicPending =
false;
941 metadata->pFirstQTD->nStatus = 0x80;
943 qh->overlay.nStatus = 0x80;
945void Ehci::destroyPeriodicCompletion(
void* context) {
946 delete static_cast<EhciCompletionCleanup*
>(context);
949void Ehci::doDequeue() {
954 if (m_DequeueStopping)
963 for (
size_t i = 1; i < EhciQhRegionBytes /
sizeof(QH); i++) {
964 if (!m_QHBitmap.
test(i))
967 QH* pQH = &m_pQHList[i];
970 if (!pQH->pMetaData) {
972 DEBUG_LOG(
"Not performing dequeue on QH #" <<
Dec << i <<
Hex
973 <<
" as it's not even initialised.");
979 if (!pQH->pMetaData->bIgnore) {
981 DEBUG_LOG(
"Not performing dequeue on QH #" <<
Dec << i <<
Hex
982 <<
" as it's still active.");
987 if (pQH->pMetaData->bPeriodic)
991 if (!pQH->pMetaData->completion.claimCaptured(claim))
993 captureCompletionLocked(i, pQH, claim, completions);
996 DEBUG_LOG(
"Dequeue for QH #" <<
Dec << i <<
Hex <<
".");
1000 if (completions.
count())
1004 while (completions.
count()) {
1005 auto* completion = completions.
popFront();
1014void Ehci::interrupt(
size_t number, InterruptState& state)
1023 if (!m_CallbackOperations.tryAcquire(callback)) {
1036 if (m_InterruptClosure == 2) {
1055 m_pBase->read32(m_nOpRegsOffset + EHCI_STS) & m_pBase->read32(m_nOpRegsOffset + EHCI_INTR);
1059 DEBUG_LOG_NOLOCK(
"EHCI: shared IRQ with no pending controller cause");
1063 IrqDisposition::NotHandled
1072 if (nStatus & EHCI_STS_PORTCH) {
1073 m_pBase->write32(EHCI_STS_PORTCH, m_nOpRegsOffset + EHCI_STS);
1074 (void)m_pBase->read32(m_nOpRegsOffset + EHCI_STS);
1079 const uint32_t immediateStatus = nStatus & ~EHCI_STS_PORTCH;
1080 if (immediateStatus) {
1081 m_pBase->write32(immediateStatus, m_nOpRegsOffset + EHCI_STS);
1082 (void)m_pBase->read32(m_nOpRegsOffset + EHCI_STS);
1085 if (nStatus & (EHCI_STS_SYSERR | EHCI_STS_ERR)) {
1086 NOTICE_NOLOCK(
"EHCI: Unusual IRQ, status is " << nStatus);
1090 DEBUG_LOG_NOLOCK(
"EHCI IRQ " << nStatus);
1093 if (nStatus & EHCI_STS_PORTCH) {
1094 constexpr uint32_t ChangeMask = EHCI_PORTSC_CSCH | EHCI_PORTSC_ENCH | EHCI_PORTSC_OCCH;
1095 for (
size_t i = 0; i < m_nPorts; i++) {
1096 const size_t portRegister = m_nOpRegsOffset + EHCI_PORTSC + i * 4;
1097 const uint32_t portStatus = m_pBase->read32(portRegister);
1098 uint32_t acknowledgeMask = portStatus & (EHCI_PORTSC_ENCH | EHCI_PORTSC_OCCH);
1100 if (portStatus & EHCI_PORTSC_CSCH) {
1101 if (deferConnectionChangeIfSuppressed(i)) {
1102 acknowledgeMask |= EHCI_PORTSC_CSCH;
1104 const auto observation = m_PortChanges[i].observe();
1105 const bool accepted = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
1113 m_InterruptClosure = 1;
1114 const uint32_t interrupts = m_pBase->read32(m_nOpRegsOffset + EHCI_INTR);
1115 m_pBase->write32(interrupts & ~EHCI_STS_PORTCH, m_nOpRegsOffset + EHCI_INTR);
1116 (void)m_pBase->read32(m_nOpRegsOffset + EHCI_INTR);
1120 acknowledgeMask |= EHCI_PORTSC_CSCH;
1121 m_DeferredPortChanges.defer(i, observation.generation);
1125 if (acknowledgeMask) {
1126 m_pBase->write32(UsbHcd::selectiveW1cValue(portStatus, ChangeMask, acknowledgeMask),
1129 (void)m_pBase->read32(portRegister);
1133 for (
size_t i = 0; i < m_nPorts; ++i) {
1134 const size_t generation = m_DeferredPortChanges.release(i);
1136 m_PortChanges[i].acknowledge(generation);
1145 if (nStatus & (EHCI_STS_INT | EHCI_STS_ERR)) {
1146 for (
size_t i = 1; i < EhciQhRegionBytes /
sizeof(
QH); i++) {
1147 if (!m_QHBitmap.test(i))
1150 QH* pQH = &m_pQHList[i];
1151 if (!pQH->pMetaData)
1154 if (!pQH->pMetaData->bPeriodic && !(pQH->pMetaData->pPrev && pQH->pMetaData->pNext))
1156 if (pQH->pMetaData->bIgnore || pQH->pMetaData->periodicPending)
1158 if (!(pQH->pMetaData->pFirstQTD && pQH->pMetaData->pLastQTD))
1161 bool bPeriodic = pQH->pMetaData->bPeriodic;
1163 size_t nQTDIndex = INDEX_FROM_QTD(pQH->pMetaData->pFirstQTD);
1164 constexpr size_t QtdCount = EhciHardwarePageBytes /
sizeof(
qTD);
1165 size_t qtdBudget = QtdCount;
1168 if (nQTDIndex >= QtdCount) {
1169 ERROR_NOLOCK(
"EHCI: QH #" <<
Dec << i <<
Hex <<
" has an out-of-range qTD pointer");
1173 qTD* pqTD = &m_pqTDList[nQTDIndex];
1175 if (!(pqTD->nStatus & 0x80) && !pqTD->processed) {
1176 pqTD->processed =
true;
1178 if (pqTD->nStatus & 0x7c) {
1180 ERROR_NOLOCK(((nStatus & EHCI_STS_ERR) ?
"USB" :
"qTD") <<
" ERROR!");
1181 ERROR_NOLOCK(
"qTD Status: " << pqTD->nStatus
1182 <<
" [overlay status=" << pQH->overlay.nStatus <<
"]");
1183 ERROR_NOLOCK(
"qTD Error Counter: " << pqTD->nErr <<
" [overlay counter="
1184 << pQH->overlay.nErr <<
"]");
1185 ERROR_NOLOCK(
"QH NAK counter: " << pqTD->res1
1186 <<
" [overlay count=" << pQH->overlay.res1 <<
"]");
1187 ERROR_NOLOCK(
"qTD PID: " << pqTD->nPid <<
".");
1189 nResult = -pqTD->getError();
1191 nResult = pqTD->nBufferSize - pqTD->nBytes;
1192 copyDmaInput(pqTD, nResult);
1193 pQH->pMetaData->nTotalBytes += nResult;
1197 "qTD #" <<
Dec << nQTDIndex <<
Hex <<
" [from QH #" <<
Dec << i <<
Hex
1198 <<
"] DONE: " <<
Dec << pQH->nAddress <<
":" << pQH->nEndpoint <<
" "
1201 : (pqTD->nPid == 1 ?
"IN" : (pqTD->nPid == 2 ?
"SETUP" :
"")))
1202 <<
" " << nResult <<
Hex);
1206 const bool shortIn =
1207 pQH->nEndpoint && pqTD->nPid == 1 && nResult >= 0 && nResult < pqTD->nBufferSize;
1208 if ((nResult < 0) || shortIn || (pqTD == pQH->pMetaData->pLastQTD)) {
1209 const ssize_t completionResult = nResult < 0 ? nResult : pQH->pMetaData->nTotalBytes;
1210 const bool ownsCompletion =
1213 if (!bPeriodic && ownsCompletion) {
1216 m_QueueListChangeLock.acquire();
1220 pQH->pMetaData->pNext->hrcl = 1;
1224 QH* pPrev = pQH->pMetaData->pPrev;
1225 QH* pNext = pQH->pMetaData->pNext;
1228 pPrev->pMetaData->pNext = pNext;
1229 pNext->pMetaData->pPrev = pPrev;
1232 pPrev->pNext = pQH->pNext;
1235 if (pQH == m_pCurrentQueueTail) {
1236 m_pCurrentQueueTail = pPrev;
1242 size_t cmdReg = m_pBase->read32(m_nOpRegsOffset + EHCI_CMD);
1243 m_pBase->write32(cmdReg | (1 << 6), m_nOpRegsOffset + EHCI_CMD);
1246 pQH->pMetaData->bIgnore =
true;
1248 m_QueueListChangeLock.release();
1251 if (bPeriodic && pQH->pMetaData->
pCallback) {
1253 pQH->pMetaData->periodicPending =
true;
1254 pQH->pMetaData->periodicTemplate.bDataToggle = pQH->overlay.bDataToggle;
1255 pQH->pMetaData->nTotalBytes = 0;
1257 new EhciCompletionCleanup{
this, i, pQH->pMetaData->periodicGeneration};
1258 completions.
pushBack(m_CompletionDeliveries.create(
1259 {i, m_CompletionDeliveries.nextGeneration(),
1260 pQH->pMetaData->periodicGeneration},
1261 pQH->pMetaData->
pCallback, pQH->pMetaData->pParam, completionResult,
1262 rearmPeriodicCompletion, rearm, destroyPeriodicCompletion, rearm));
1264 pQH->pMetaData->
pCallback(pQH->pMetaData->pParam, completionResult);
1270 size_t oldIndex = nQTDIndex;
1272 if (pqTD->bNextInvalid)
1275 nQTDIndex = ((pqTD->pNext << 5) & EhciDescriptorOffsetMask) /
sizeof(
qTD);
1277 if (nQTDIndex == oldIndex) {
1278 ERROR_NOLOCK(
"EHCI: QH #" <<
Dec << i <<
Hex
1279 <<
"'s qTD list is invalid - circular reference!");
1281 }
else if (pqTD->pNext == 0) {
1282 ERROR_NOLOCK(
"EHCI: QH #" <<
Dec << i <<
Hex
1283 <<
"'s qTD list is invalid - null pNext "
1284 "pointer (and T bit not set)!");
1289 ERROR_NOLOCK(
"EHCI: QH #" <<
Dec << i <<
Hex <<
" exceeded the qTD scan budget");
1296 if (nStatus & EHCI_STS_ASYNCADVANCE) {
1297 m_DequeueCount.release();
1301 if (completions.
count())
1302 m_CompletionDeliveries.publish(completions);
1305 while (completions.
count()) {
1306 auto* completion = completions.
popFront();
1307 m_CompletionDeliveries.deliver(completion);
1309 return IrqDisposition::Handled;
1314 uintptr_t pBuffer,
size_t nBytes) {
1318 addTransferToTransactionAdmitted(nTransaction, bToggle, pid, pBuffer, nBytes);
1321void Ehci::addTransferToTransactionAdmitted(uintptr_t nTransaction,
bool bToggle, UsbPid pid,
1322 uintptr_t pBuffer,
size_t nBytes) {
1324 constexpr size_t QhCount = EhciQhRegionBytes /
sizeof(QH);
1325 if (nTransaction >= QhCount || !m_QHBitmap.
test(nTransaction)) {
1326 ERROR(
"EHCI: addTransferToTransaction: invalid transaction");
1330 QH* pQH = &m_pQHList[nTransaction];
1331 if (!pQH->pMetaData || pQH->pMetaData->bBuildFailed ||
1332 pQH->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle) {
1333 ERROR(
"EHCI: addTransferToTransaction: transaction is not building");
1337 qTD* last = pQH->pMetaData->pLastQTD;
1338 if (last && !last->pDma && pid != UsbPidSetup && nBytes &&
1339 last->nPid == (pid == UsbPidIn ? 1U : 0U) && last->nBufferSize &&
1340 !(last->nBufferSize % pQH->nMaxPacketSize) &&
1341 last->nOffset + last->nBufferSize + nBytes <= EhciHardwarePageBytes) {
1342 physical_uintptr_t physical = 0;
1344 if (DriverDma::virtualToPhysical(addressSpace, pBuffer, physical) &&
1346 (
static_cast<uintptr_t
>(last->pPage0) << 12) + last->nOffset + last->nBufferSize) {
1347 last->nBytes += nBytes;
1348 last->nBufferSize += nBytes;
1349 if (last == pQH->pMetaData->pFirstQTD)
1350 MemoryCopy(&pQH->overlay, last,
sizeof(qTD));
1355 if (nIndex >= (EhciHardwarePageBytes /
sizeof(qTD))) {
1356 ERROR(
"USB: EHCI: qTD space full");
1357 pQH->pMetaData->bBuildFailed =
true;
1360 m_qTDBitmap.
set(nIndex);
1363 qTD* pqTD = &m_pqTDList[nIndex];
1364 ByteSet(pqTD, 0,
sizeof(qTD));
1367 pqTD->bNextInvalid = 1;
1368 pqTD->bAltNextInvalid = 1;
1386 pqTD->nStatus = 0x80;
1387 pqTD->bIoc = pQH->nEndpoint && pid == UsbPidIn;
1391 pqTD->nBytes = nBytes;
1392 pqTD->nBufferSize = nBytes;
1393 pqTD->bDataToggle = bToggle;
1398 const size_t nBufferPageOffset = pBuffer & EhciDescriptorOffsetMask;
1399 const uintptr_t pBufferPageStart = pBuffer & ~uintptr_t(EhciDescriptorOffsetMask);
1400 pqTD->nOffset = nBufferPageOffset;
1402 const uintptr_t highestVirtual =
static_cast<uintptr_t
>(-1);
1403 if ((nBytes - 1) > highestVirtual - pBuffer) {
1404 ERROR(
"EHCI: addTransferToTransaction: buffer range wraps");
1405 pQH->pMetaData->bBuildFailed =
true;
1406 m_qTDBitmap.
clear(nIndex);
1407 ByteSet(pqTD, 0,
sizeof(qTD));
1411 if (nBytes > EhciQtdBufferWindowBytes - nBufferPageOffset) {
1413 "EHCI: addTransferToTransaction: Too many bytes for a single "
1415 pQH->pMetaData->bBuildFailed =
true;
1416 m_qTDBitmap.
clear(nIndex);
1417 ByteSet(pqTD, 0,
sizeof(qTD));
1422 uint32_t bufferPages[EhciQtdBufferPageCount] = {};
1423 const size_t bufferSpan = nBufferPageOffset + nBytes;
1425 bool dmaAddressable =
true;
1426 for (
size_t page = 0; page < EhciQtdBufferPageCount; ++page) {
1427 if (bufferSpan <= page * EhciHardwarePageBytes)
1430 physical_uintptr_t physicalPage = 0;
1431 const uintptr_t virtualPage = pBufferPageStart + page * EhciHardwarePageBytes;
1432 if (!DriverDma::virtualToPhysical(va, virtualPage, physicalPage)) {
1436 if ((physicalPage & EhciDescriptorOffsetMask) ||
1437 !DriverDma::physicalRangeFits(physicalPage, EhciHardwarePageBytes,
1438 EhciHighestDmaAddress)) {
1439 dmaAddressable =
false;
1441 bufferPages[page] = physicalPage >> EhciHardwarePageShift;
1446 ERROR(
"EHCI: addTransferToTransaction: buffer is not mapped");
1447 pQH->pMetaData->bBuildFailed =
true;
1448 m_qTDBitmap.
clear(nIndex);
1449 ByteSet(pqTD, 0,
sizeof(qTD));
1453 if (!dmaAddressable) {
1454 auto* region =
new MemoryRegion(
"EHCI transfer bounce");
1455 const size_t pages = DriverDma::pageCountForBytes(nBytes, EhciHardwarePageBytes);
1461 ERROR(
"EHCI: addTransferToTransaction: couldn't allocate a below-4GiB bounce buffer");
1462 pQH->pMetaData->bBuildFailed =
true;
1463 m_qTDBitmap.
clear(nIndex);
1464 ByteSet(pqTD, 0,
sizeof(qTD));
1468 auto* transfer =
new EhciDmaTransfer{region, pBuffer, nBytes, pid == UsbPidIn};
1470 !DriverDma::physicalRangeFits(region->physicalAddress(), pages * EhciHardwarePageBytes,
1471 EhciHighestDmaAddress)) {
1474 ERROR(
"EHCI: addTransferToTransaction: bounce buffer is not DMA-addressable");
1475 pQH->pMetaData->bBuildFailed =
true;
1476 m_qTDBitmap.
clear(nIndex);
1477 ByteSet(pqTD, 0,
sizeof(qTD));
1481 pqTD->pDma =
reinterpret_cast<uintptr_t
>(transfer);
1483 if (pid != UsbPidIn)
1484 MemoryCopy(region->virtualAddress(),
reinterpret_cast<void*
>(pBuffer), nBytes);
1485 for (
size_t page = 0; page < EhciQtdBufferPageCount; ++page) {
1486 if (nBytes <= page * EhciHardwarePageBytes)
1488 const physical_uintptr_t physicalPage =
1489 region->physicalAddress() + page * EhciHardwarePageBytes;
1490 bufferPages[page] = physicalPage >> EhciHardwarePageShift;
1493 DEBUG_LOG(
"EHCI: using a below-4GiB bounce buffer for " <<
Dec << nBytes <<
" bytes");
1497 pqTD->pPage0 = bufferPages[0];
1498 pqTD->pPage1 = bufferPages[1];
1499 pqTD->pPage2 = bufferPages[2];
1500 pqTD->pPage3 = bufferPages[3];
1501 pqTD->pPage4 = bufferPages[4];
1504 if (!pQH->nEndpoint && !nBytes && pQH->pMetaData->pFirstQTD) {
1505 qTD* previous = pQH->pMetaData->pFirstQTD;
1507 if (previous->nPid == 1) {
1508 previous->pAltNext = PHYS_QTD(nIndex) >> 5;
1509 previous->bAltNextInvalid = 0;
1511 if (previous->bNextInvalid)
1513 previous = &m_pqTDList[((previous->pNext << 5) - m_pqTDListPhys) /
sizeof(qTD)];
1517 if (pQH->pMetaData->pLastQTD) {
1518 pQH->pMetaData->pLastQTD->pNext = PHYS_QTD(nIndex) >> 5;
1519 pQH->pMetaData->pLastQTD->bNextInvalid = 0;
1521 if (pQH->pMetaData->pLastQTD == pQH->pMetaData->pFirstQTD) {
1522 pQH->overlay.pNext = pQH->pMetaData->pLastQTD->pNext;
1523 pQH->overlay.bNextInvalid = pQH->pMetaData->pLastQTD->bNextInvalid;
1526 pQH->pMetaData->pFirstQTD = pqTD;
1527 pQH->pQTD = PHYS_QTD(nIndex) >> 5;
1528 MemoryCopy(&pQH->overlay, pqTD,
sizeof(qTD));
1530 pQH->pMetaData->pLastQTD = pqTD;
1536 return static_cast<uintptr_t
>(-1);
1537 return createTransactionAdmitted(endpointInfo);
1540uintptr_t Ehci::createTransactionAdmitted(
UsbEndpoint endpointInfo) {
1543 if (nIndex >= (EhciQhRegionBytes /
sizeof(QH))) {
1544 ERROR(
"USB: EHCI: QH space full");
1545 return static_cast<uintptr_t
>(-1);
1547 m_QHBitmap.
set(nIndex);
1549 QH* pQH = &m_pQHList[nIndex];
1550 ByteSet(pQH, 0,
sizeof(QH));
1556 pQH->nNakReload = 15;
1562 pQH->nHubAddress = endpointInfo.speed != HighSpeed ? endpointInfo.nHubAddress : 0;
1563 pQH->nHubPort = endpointInfo.speed != HighSpeed ? endpointInfo.nHubPort : 0;
1564 pQH->bControlEndpoint = (endpointInfo.speed != HighSpeed) && !endpointInfo.nEndpoint;
1567 pQH->bDataToggleSrc = 1;
1570 pQH->nAddress = endpointInfo.nAddress;
1571 pQH->nSpeed = endpointInfo.speed == LowSpeed ? 1 : endpointInfo.speed == FullSpeed ? 0 : 2;
1574 pQH->nEndpoint = endpointInfo.nEndpoint;
1575 pQH->nMaxPacketSize = endpointInfo.nMaxPacketSize;
1581 pQH->pMetaData =
new QH::MetaData();
1587bool Ehci::doAsync(uintptr_t nTransaction,
void (*pCallback)(uintptr_t, ssize_t),
1594 constexpr size_t QhCount = EhciQhRegionBytes /
sizeof(
QH);
1595 if ((nTransaction ==
static_cast<uintptr_t
>(-1)) || nTransaction >= QhCount ||
1596 !m_QHBitmap.
test(nTransaction)) {
1597 ERROR(
"EHCI: doAsync: didn't get a valid transaction id [" << nTransaction <<
"].");
1601 QH* pQH = &m_pQHList[nTransaction];
1602 if (pQH->pMetaData &&
1603 pQH->pMetaData->
completion.state() != UsbHcd::TransferCompletion::State::Idle) {
1604 ERROR(
"EHCI: doAsync: transaction is already submitted");
1607 if (!pQH->pMetaData || pQH->pMetaData->bBuildFailed || !pQH->pMetaData->pLastQTD ||
1608 pQH->pMetaData->bPeriodic) {
1609 ERROR(
"EHCI: doAsync: transaction could not be submitted [" << nTransaction <<
"].");
1610 reclaimQhLocked(nTransaction);
1615 ERROR(
"EHCI: asynchronous schedule is closed");
1616 reclaimQhLocked(nTransaction);
1621 pQH->pMetaData->pLastQTD->bIoc = 1;
1624 if (pQH->pMetaData->pFirstQTD == pQH->pMetaData->pLastQTD) {
1626 pQH->overlay.bIoc = 1;
1630 DEBUG_LOG(
"START #" <<
Dec << nTransaction <<
Hex <<
" " <<
Dec << pQH->nAddress <<
":"
1631 << pQH->nEndpoint <<
Hex);
1639 const size_t queueHeadIndex = m_pCurrentQueueHead - m_pQHList;
1640 pQH->pNext = (m_pQHListPhys + (queueHeadIndex *
sizeof(
QH))) >> 5;
1641 pQH->pMetaData->pNext = m_pCurrentQueueHead;
1642 pQH->pMetaData->pPrev = m_pCurrentQueueTail;
1643 QH* pOldTail = m_pCurrentQueueTail;
1651 m_pCurrentQueueTail = pQH;
1652 pOldTail->pNext = (m_pQHListPhys + (nTransaction *
sizeof(
QH))) >> 5;
1653 pOldTail->nNextType = 1;
1654 pOldTail->pMetaData->pNext = pQH;
1657 m_pCurrentQueueHead->hrcl = 1;
1668 bool drainDelivery =
false;
1675 uint32_t savedInterrupts = 0;
1676 uint32_t savedCommand = 0;
1677 if (!teardownHalted && m_pBase && m_nOpRegsOffset) {
1678 savedInterrupts = m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
1679 savedCommand = m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD);
1680 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_INTR);
1681 m_pBase->
write32(savedCommand & ~EHCI_CMD_RUN, m_nOpRegsOffset + EHCI_CMD);
1682 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EHCI_STS_HALTED,
1685 "EHCI cancellation could not establish the DMA halt "
1693 constexpr size_t QhCount = EhciQhRegionBytes /
sizeof(
QH);
1694 if ((nTransaction !=
static_cast<uintptr_t
>(-1)) && nTransaction < QhCount &&
1695 m_QHBitmap.
test(nTransaction)) {
1696 QH* pQH = &m_pQHList[nTransaction];
1698 if (metadata && !metadata->bPeriodic) {
1700 const auto disposition =
1702 if (disposition == UsbHcd::TransferCompletion::CancellationDisposition::Claimed) {
1703 if (!metadata->bIgnore) {
1705 QH* pPrev = metadata->pPrev;
1706 QH* pNext = metadata->pNext;
1707 if (pPrev && pNext) {
1710 pPrev->pMetaData->pNext = pNext;
1711 pNext->pMetaData->pPrev = pPrev;
1712 pPrev->pNext = pQH->pNext;
1713 if (pQH == m_pCurrentQueueTail)
1714 m_pCurrentQueueTail = pPrev;
1716 metadata->bIgnore =
true;
1718 deliveryKey = {nTransaction, claim.generation};
1719 captureCompletionLocked(nTransaction, pQH, claim, completions);
1720 }
else if (disposition ==
1721 UsbHcd::TransferCompletion::CancellationDisposition::DrainPublished) {
1722 deliveryKey = {nTransaction, claim.generation};
1723 drainDelivery =
true;
1729 if (completions.
count())
1733 if (!teardownHalted && m_pBase && m_nOpRegsOffset) {
1734 m_pBase->
write32(savedCommand, m_nOpRegsOffset + EHCI_CMD);
1735 if (savedCommand & EHCI_CMD_RUN) {
1736 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EHCI_STS_HALTED, 0)) {
1738 "EHCI cancellation could not establish the DMA "
1739 "restart boundary");
1744 uint32_t restoredInterrupts = savedInterrupts;
1746 restoredInterrupts &= ~EHCI_STS_PORTCH;
1747 m_pBase->
write32(restoredInterrupts, m_nOpRegsOffset + EHCI_INTR);
1748 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
1753 if (completions.
count()) {
1754 auto* completion = completions.
popFront();
1757 }
else if (drainDelivery)
1762 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
1763 bool producerAlreadyStopped) {
1767 panic(
"EHCI interrupt-IN cancellation raced controller teardown");
1769 if (!producerAlreadyStopped) {
1770 bool matched =
false;
1775 uint32_t savedInterrupts = 0;
1776 uint32_t savedCommand = 0;
1777 if (!teardownHalted && m_pBase && m_nOpRegsOffset) {
1779 savedInterrupts = m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
1780 savedCommand = m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD);
1781 m_pBase->
write32(0, m_nOpRegsOffset + EHCI_INTR);
1782 m_pBase->
write32(savedCommand & ~EHCI_CMD_RUN, m_nOpRegsOffset + EHCI_CMD);
1783 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EHCI_STS_HALTED,
1785 panic(
"EHCI interrupt-IN cancellation could not halt DMA");
1792 constexpr size_t QhCount = EhciQhRegionBytes /
sizeof(
QH);
1793 if (token.transaction < QhCount && m_QHBitmap.
test(token.transaction)) {
1794 QH* pQH = &m_pQHList[token.transaction];
1796 if (metadata && metadata->bPeriodic && metadata->periodicGeneration == token.generation &&
1797 metadata->
pCallback == callback && metadata->pParam == parameter) {
1798 metadata->bIgnore =
true;
1800 metadata->pParam = 0;
1801 reclaimQhLocked(token.transaction);
1802 rebuildPeriodicScheduleLocked();
1808 if (!teardownHalted && m_pBase && m_nOpRegsOffset) {
1810 m_pBase->
write32(savedCommand, m_nOpRegsOffset + EHCI_CMD);
1812 if ((savedCommand & EHCI_CMD_RUN) &&
1813 !waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, EHCI_STS_HALTED, 0)) {
1814 panic(
"EHCI interrupt-IN cancellation could not restart DMA");
1818 uint32_t restoredInterrupts = savedInterrupts;
1820 restoredInterrupts &= ~EHCI_STS_PORTCH;
1821 m_pBase->
write32(restoredInterrupts, m_nOpRegsOffset + EHCI_INTR);
1822 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
1827 panic(
"EHCI interrupt-IN handle lost its controller subscription");
1834 panic(
"non-x86 EHCI unexpectedly owned an interrupt-IN handle");
1840 void (*pCallback)(uintptr_t, ssize_t),
1849 ERROR(
"USB: non-x86 EHCI recurring interrupt-IN is not safely supported");
1856 if (!endpointInfo.nInterval || !nBytes || nBytes > endpointInfo.nMaxPacketSize ||
1857 (endpointInfo.speed == HighSpeed && endpointInfo.nInterval > 14) ||
1858 (endpointInfo.speed != HighSpeed && (!endpointInfo.nHubAddress || !endpointInfo.nHubPort)))
1860 size_t microframes = endpointInfo.speed == HighSpeed ?
size_t{1} << (endpointInfo.nInterval - 1)
1861 :
size_t{8} * endpointInfo.nInterval;
1862 size_t interval = 1;
1863 while (interval < 1024 && interval * 16 <= microframes)
1866 uintptr_t nTransaction = createTransactionAdmitted(endpointInfo);
1867 if (nTransaction ==
static_cast<uintptr_t
>(-1)) {
1875 m_pQHList[nTransaction].pMetaData->bPeriodic =
true;
1879 addTransferToTransactionAdmitted(nTransaction,
false, UsbPidIn, pBuffer, nBytes);
1884 QH* pQH = &m_pQHList[nTransaction];
1885 if (!pQH->pMetaData || pQH->pMetaData->bBuildFailed || !pQH->pMetaData->pLastQTD ||
1887 ERROR(
"USB: EHCI: Couldn't add interrupt transfer!");
1888 reclaimQhLocked(nTransaction);
1892 uint8_t startMask = 1, completeMask = 0;
1893 if (endpointInfo.speed == HighSpeed) {
1894 const size_t step = microframes < 8 ? microframes : 8;
1896 for (
size_t microframe = nTransaction % step; microframe < 8; microframe += step)
1897 startMask |= 1U << microframe;
1899 uint8_t occupied = 0;
1900 for (
size_t i = 1; i < EhciQhRegionBytes /
sizeof(
QH); ++i) {
1901 auto* other = &m_pQHList[i];
1902 if (i != nTransaction && m_QHBitmap.
test(i) && other->pMetaData &&
1903 other->pMetaData->bPeriodic && other->pMetaData->pCallback &&
1904 other->nHubAddress == endpointInfo.nHubAddress)
1905 occupied |= other->ism;
1907 size_t microframe = 0;
1908 while (microframe < 4 && (occupied & (1U << microframe)))
1910 if (microframe == 4) {
1911 reclaimQhLocked(nTransaction);
1914 startMask = 1U << microframe;
1915 completeMask = 0x1cU << microframe;
1919 for (
size_t microframe = 0; microframe < 8; ++microframe) {
1920 size_t nanoseconds = 0;
1921 for (
size_t i = 1; i < EhciQhRegionBytes /
sizeof(
QH); ++i) {
1922 auto* other = &m_pQHList[i];
1923 if (!m_QHBitmap.
test(i) || !other->pMetaData || !other->pMetaData->bPeriodic ||
1924 (i != nTransaction && !other->pMetaData->pCallback))
1926 const uint8_t starts = i == nTransaction ? startMask : other->ism;
1927 const uint8_t completes = i == nTransaction ? completeMask : other->scm;
1928 if ((starts | completes) & (1U << microframe))
1929 nanoseconds += other->nSpeed == 2 ? other->nMaxPacketSize * 20U + 3000U : 20000U;
1931 if (nanoseconds > 80000) {
1932 reclaimQhLocked(nTransaction);
1936 const uint32_t savedCommand = m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD);
1937 m_pBase->
write32(savedCommand & ~EHCI_CMD_PERIODICLE, m_nOpRegsOffset + EHCI_CMD);
1938 if (!waitForMmioState(m_pBase, m_nOpRegsOffset + EHCI_STS, 1U << 14, 0))
1939 panic(
"EHCI periodic insertion could not stop the periodic schedule");
1942 pQH->nNakReload = 0;
1943 pQH->ism = startMask;
1944 pQH->scm = completeMask;
1945 pQH->pMetaData->pLastQTD->nErr = 3;
1946 pQH->pMetaData->pLastQTD->bIoc = 1;
1947 pQH->pMetaData->periodicTemplate = *pQH->pMetaData->pLastQTD;
1948 MemoryCopy(&pQH->overlay, pQH->pMetaData->pLastQTD,
sizeof(
qTD));
1950 pQH->pMetaData->pParam = pParam;
1952 pQH->pMetaData->periodicFrameIndex = 0;
1953 pQH->pMetaData->periodicInterval = interval;
1955 handle, {nTransaction, pQH->pMetaData->periodicGeneration}, pCallback, pParam);
1957 panic(
"EHCI submitted interrupt-IN without publishing its owner handle");
1958 rebuildPeriodicScheduleLocked();
1959 m_pBase->
write32(savedCommand | EHCI_CMD_PERIODICLE, m_nOpRegsOffset + EHCI_CMD);
1960 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_CMD);
1968 if (port >= m_nPorts) {
1969 ERROR(
"EHCI: invalid suppressed root-port replay " <<
Dec << port);
1978 const auto observation = m_PortChanges[port].observe();
1979 const bool accepted = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
1981 m_PortChanges[port].acknowledge(observation.generation);
1986 const uint32_t interrupts = m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
1987 m_pBase->
write32(interrupts & ~EHCI_STS_PORTCH, m_nOpRegsOffset + EHCI_INTR);
1988 (void)m_pBase->
read32(m_nOpRegsOffset + EHCI_INTR);
1989 ERROR(
"EHCI: live suppressed root-port replay could not be published");
1996void Ehci::modifyPortControl(
size_t portRegister, uint32_t clearMask, uint32_t setMask) {
1999 control &= ~clearMask;
2001 m_pBase->
write32(control, portRegister);
2002 (void)m_pBase->
read32(portRegister);
2007 if (!startup || !m_HardwareOwned || nPort >= m_nPorts)
2010 const size_t portRegister = m_nOpRegsOffset + EHCI_PORTSC + (nPort * 4);
2011 if (bErrorResponse) {
2012 modifyPortControl(portRegister, EHCI_PORTSC_EN, 0);
2013 if (!waitForMmioState(m_pBase, portRegister, EHCI_PORTSC_EN, 0)) {
2014 ERROR(
"EHCI: port " <<
Dec << nPort <<
Hex <<
" did not disable within 100 ms");
2020 for (retry = 0; retry < 3; retry++) {
2022 DEBUG_LOG(
"USB: EHCI: Port " <<
Dec << nPort <<
Hex <<
" - status before reset: "
2023 << m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + (nPort * 4)));
2027 modifyPortControl(portRegister, 0, EHCI_PORTSC_PRES);
2029 Time::delay(50 * Time::Multiplier::Millisecond);
2032 modifyPortControl(portRegister, EHCI_PORTSC_PRES, 0);
2035 if (!waitForMmioState(m_pBase, portRegister, EHCI_PORTSC_PRES, 0)) {
2036 ERROR(
"EHCI: reset on port " <<
Dec << nPort <<
Hex <<
" did not clear within 100 ms");
2041 DEBUG_LOG(
"USB: EHCI: Port " <<
Dec << nPort <<
Hex <<
" - status after reset: "
2042 << m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + (nPort * 4)));
2045 if ((m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + (nPort * 4)) & EHCI_PORTSC_EN) &&
2046 (m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + (nPort * 4)) & EHCI_PORTSC_CONN)) {
2047 DEBUG_LOG(
"USB: EHCI: Port " <<
Dec << nPort <<
Hex <<
" is connected");
2050 DEBUG_LOG(
"USB: EHCI: Port " <<
Dec << nPort <<
Hex
2051 <<
" seems to be not HighSpeed. Returning "
2052 "to companion controllers.");
2053 modifyPortControl(portRegister, 0, 0x2000);
2057 WARNING(
"EHCI: Port " <<
Dec << nPort <<
Hex <<
" could not be connected");
2063 uint64_t p6, uint64_t p7, uint64_t p8) {
2064 if (p1 >= m_nPorts) {
2072 struct InitialPortCompletion {
2075 ~InitialPortCompletion() {
2076 controller->completeInitialPort(port);
2078 } initial{
this,
static_cast<size_t>(p1)};
2084 if (m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + p1 * 4) & EHCI_PORTSC_CONN) {
2087 WARNING(
"EHCI: Port " <<
Dec << p1 <<
Hex
2088 <<
" appeared to be connected but could not be set up");
2092 const uint32_t port = m_pBase->
read32(m_nOpRegsOffset + EHCI_PORTSC + p1 * 4);
2094 if ((port & EHCI_PORTSC_CONN) && !(port & (1U << 13)))
2098 DEBUG_LOG(
"USB: EHCI: Port " <<
Dec << p1 <<
Hex <<
" is disconnected");
2106 if (request.p1 < m_nPorts) {
2107 m_PortChanges[request.p1].cancel(
static_cast<size_t>(request.p8));
2108 completeInitialPort(request.p1);
void map(size_t forcedSize=0, bool bUser=false, bool bWriteCombine=false, bool bWriteThrough=false)
virtual Vector< Address * > & addresses()
Device * getParent() const
virtual uintptr_t getInterruptNumber()
UsbHcd::CallbackDeliveryQueue m_CompletionDeliveries
MUST_USE_RESULT bool cancelInterruptInAndDrain(const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter, bool producerAlreadyStopped) override
void replaySuppressedConnectionChange(size_t port) override
IrqDisposition irq(irq_id_t number) override
IRQ handler.
static uint32_t portControlValue(uint32_t value)
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)
Adds a new transfer to an existent transaction.
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
IrqProcessingLock m_IrqProcessingLock
Atomic< size_t > m_InterruptClosure
OperationBarrier m_SubmissionOperations
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
virtual uint64_t executeRequest(uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
OperationBarrier m_CallbackOperations
OperationBarrier m_CancelOperations
virtual MUST_USE_RESULT bool addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes, void(*pCallback)(uintptr_t, ssize_t), UsbInterruptInHandle &handle, uintptr_t pParam=0)
Adds an owned recurring interrupt-IN transaction.
void cancelRequest(const Request &request) override
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
bool test(size_t n) const
Abstrace base class for hardware I/O capabilities.
virtual void write32(uint32_t value, size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
virtual size_t size() const =0
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &policy)=0
virtual bool control(uint8_t irq, ControlCode code, size_t argument)
static void setShutdownDetail(const char *detail)
Special memory entity in the kernel's virtual address space.
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
MUST_USE_RESULT bool tryAcquire(Lease &lease)
static const size_t continuous
static PhysicalMemoryManager & instance()
static const size_t below4GB
static ProcessorInformation & information()
LifecycleState getLifecycleState()
virtual void initialise()
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
static constexpr size_t getPageSize() noexcept
void deliver(Record *record)
bool drain(const Key &key)
bool cancelSubscription(uintptr_t transaction, size_t subscription)
void publish(List< Record * > &records)
MUST_USE_RESULT CancellationDisposition claimCancellation(Callback callback, uintptr_t parameter, ssize_t cancellationResult, Claim &claim)
void arm(Callback callback, uintptr_t parameter, size_t generation)
MUST_USE_RESULT bool captureNatural(ssize_t result)
bool deviceConnected(uint8_t nPort, UsbSpeed speed)
Called when a device is connected to a port on the hub.
void deviceDisconnected(uint8_t nPort)
Called when a device is disconnected from a port on the hub.
MUST_USE_RESULT bool publishInterruptInHandle(UsbInterruptInHandle &handle, const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter)
void disconnectAllDevices()
static const size_t KernelMode
static const size_t Write
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
void pushBack(const T &value)