23#include "pedigree/kernel/LockGuard.h"
24#include "pedigree/kernel/Log.h"
25#include "pedigree/kernel/machine/Device.h"
26#include "pedigree/kernel/machine/IrqManager.h"
27#include "pedigree/kernel/machine/Machine.h"
28#include "pedigree/kernel/machine/Pci.h"
29#include "pedigree/kernel/machine/PciFunctionState.h"
30#include "pedigree/kernel/machine/Timer.h"
31#include "pedigree/kernel/panic.h"
32#include "pedigree/kernel/process/TerminationDeferral.h"
33#include "pedigree/kernel/process/Thread.h"
34#include "pedigree/kernel/processor/IoBase.h"
35#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
36#include "pedigree/kernel/processor/Processor.h"
37#include "pedigree/kernel/processor/ProcessorInformation.h"
38#include "pedigree/kernel/processor/VirtualAddressSpace.h"
39#include "pedigree/kernel/time/Time.h"
40#include "pedigree/kernel/utilities/Iterator.h"
41#include "pedigree/kernel/utilities/Vector.h"
42#include "pedigree/kernel/utilities/utility.h"
44#include "modules/drivers/common/DmaBuffer.h"
45#include "modules/system/usb/Usb.h"
47#define INDEX_FROM_TD_VIRT(ptr) \
48 (((reinterpret_cast<uintptr_t>((ptr)) - reinterpret_cast<uintptr_t>(m_pTDList)) / sizeof(TD)))
49#define INDEX_FROM_TD_PHYS(ptr) ((((ptr) - m_pTDListPhys) / sizeof(TD)))
50#define PHYS_TD(idx) (m_pTDListPhys + ((idx) * sizeof(TD)))
53constexpr size_t UhciHardwareFrameListBytes = 4096;
54constexpr size_t UhciQhRegionBytes = 0x4000;
55constexpr size_t UhciTdRegionBytes = 0x8000;
56constexpr size_t UhciDmaRegionBytes =
57 UhciHardwareFrameListBytes + UhciQhRegionBytes + UhciTdRegionBytes;
58constexpr size_t UhciMaximumTransferBytes = 0x800;
59constexpr physical_uintptr_t UhciHighestDmaAddress = 0xffffffff;
60struct PeriodicCompletion {
62 uintptr_t transaction;
67static int threadStub(
void* p) {
69 Uhci* pUhci =
reinterpret_cast<Uhci*
>(p);
78 m_SubmissionOperations(),
80 m_TransferOperations(),
81 m_CallbackOperations(),
83 m_TimerRegistered(false),
84 m_InterruptsClosing(false),
86 m_PortChangesClosing(false),
89 m_IrqProcessingLock(),
90 m_CompletionDeliveries(),
91 m_AsyncQueueListChangeLock(),
92 m_pFrameList(nullptr),
97 m_pPeriodicQH(nullptr),
101 m_pCurrentAsyncQueueTail(0),
102 m_pCurrentAsyncQueueHead(0),
104 m_PeriodicSchedule(),
107 m_DequeueOperations(),
108 m_pDequeueThread(nullptr),
109 m_nPortCheckTicks(0) {
110 setSpecificType(
String(
"UHCI"));
116 auto& pci = PciBus::instance();
118 if (!pci.inspectFunction(
this, pciState)) {
119 ERROR(
"UHCI: unsupported inherited PCI function state");
122 const uint32_t bar = pciState.bars[4];
123 const uint32_t base = bar & ~3U;
125 for (
auto* address : addresses())
126 if (address->m_Name ==
"bar4" && (bar & 1U) && base && address->m_IsIoSpace &&
127 address->m_Address == base)
129 if (!mapping || mapping->
m_Size < 0x20 || !pci.updateCommand(
this, 0, 1))
132 m_pBase = mapping->
m_Io;
138 ERROR(
"USB: UHCI: target pages cannot align the 4 KiB hardware frame list");
144 m_UhciMR, DriverDma::pageCountForBytes(UhciDmaRegionBytes),
147 ERROR(
"USB: UHCI: Couldn't allocate memory region!");
151 uintptr_t pVirtBase =
reinterpret_cast<uintptr_t
>(m_UhciMR.virtualAddress());
152 physical_uintptr_t pPhysBase = m_UhciMR.physicalAddress();
154 m_pFrameList =
reinterpret_cast<uint32_t*
>(pVirtBase);
155 m_pQHList =
reinterpret_cast<QH*
>(pVirtBase + UhciHardwareFrameListBytes);
156 m_pTDList =
reinterpret_cast<TD*
>(pVirtBase + UhciHardwareFrameListBytes + UhciQhRegionBytes);
158 m_pFrameListPhys = pPhysBase;
159 m_pQHListPhys = pPhysBase + UhciHardwareFrameListBytes;
160 m_pTDListPhys = m_pQHListPhys + UhciQhRegionBytes;
164 QH* pDummyQH = &m_pQHList[0];
167 DoubleWordSet(m_pFrameList, m_pQHListPhys | 2, 0x400);
169 ByteSet(pDummyQH, 0,
sizeof(QH));
171 pDummyQH->pNext = m_pQHListPhys >> 4;
172 pDummyQH->bNextQH = 1;
173 pDummyQH->bElemInvalid = 1;
175 pDummyQH->pMetaData =
new QH::MetaData;
176 pDummyQH->pMetaData->pOwner =
this;
177 pDummyQH->pMetaData->pPeriodicCallback =
nullptr;
178 pDummyQH->pMetaData->pPeriodicParam = 0;
179 pDummyQH->pMetaData->periodicGeneration = 0;
180 pDummyQH->pMetaData->periodicInterval = 0;
181 pDummyQH->pMetaData->periodicBusTime = 0;
182 pDummyQH->pMetaData->periodicPending =
false;
183 pDummyQH->pMetaData->bPeriodic =
false;
184 pDummyQH->pMetaData->bBuildFailed =
false;
185 pDummyQH->pMetaData->pFirstTD =
nullptr;
186 pDummyQH->pMetaData->pLastTD =
nullptr;
187 pDummyQH->pMetaData->nTotalBytes = 0;
188 pDummyQH->pMetaData->bIgnore =
true;
189 pDummyQH->pMetaData->id = 0;
190 pDummyQH->pMetaData->pPrev = pDummyQH->pMetaData->pNext = pDummyQH;
192 m_pCurrentAsyncQueueTail = m_pCurrentAsyncQueueHead = pDummyQH;
196 threadStub,
reinterpret_cast<void*
>(
this));
199 if (!setLegacySupportControl(0x8f00)) {
200 ERROR(
"UHCI: legacy SMI sources did not disable");
203 m_HardwareOwned =
true;
204 m_pBase->write16(0, UHCI_INTR);
205 (void)m_pBase->read16(UHCI_INTR);
209 m_pBase->write16(m_pBase->read16(UHCI_CMD) & ~0x40, UHCI_CMD);
211 m_pBase->write16(m_pBase->read16(UHCI_STS) & 0x1f, UHCI_STS);
214 m_pBase->write16(m_pBase->read16(UHCI_CMD) | UHCI_CMD_HCRES, UHCI_CMD);
215 constexpr size_t ResetPollLimit = 100;
216 size_t resetPolls = ResetPollLimit;
217 while (resetPolls-- && (m_pBase->read16(UHCI_CMD) & UHCI_CMD_HCRES))
218 Time::delay(1 * Time::Multiplier::Millisecond);
219 if (m_pBase->read16(UHCI_CMD) & UHCI_CMD_HCRES)
220 panic(
"UHCI controller reset did not complete within 100 ms");
222 if (!pci.updateCommand(
this, 4, 1 | 0x400) || !pci.disableMessageInterrupts(
this, pciState) ||
223 !pci.resourcesUnchanged(
this, pciState)) {
224 ERROR(
"UHCI: PCI interrupt state or resources changed during handoff");
228 if (!pci.updateCommand(
this, 0, 5 | 0x400))
232 m_pBase->write32(m_pFrameListPhys, UHCI_FRLP);
236 m_pBase->write16(0, UHCI_INTR);
237 (void)m_pBase->read16(UHCI_INTR);
238 const uint16_t pendingStatus = m_pBase->read16(UHCI_STS);
240 m_pBase->write16(pendingStatus, UHCI_STS);
241 (void)m_pBase->read16(UHCI_STS);
245 static_cast<IrqHandler*
>(
this),
this, IrqPolicy::pciIntxThreaded());
247 panic(
"UHCI could not register its PCI IRQ handler");
249 Machine::instance().getIrqManager()->
control(
250 getInterruptNumber(), IrqManager::MitigationThreshold,
256 if (getLifecycleState() != RequestQueue::LifecycleState::Accepting) {
257 panic(
"UHCI request queue did not enter the accepting state");
263 m_pBase->write16(0xC1 | 0x10, UHCI_CMD);
264 Time::delay(10 * Time::Multiplier::Millisecond);
265 m_pBase->write16(0xC1, UHCI_CMD);
269 DEBUG_LOG(
"USB: UHCI: Reset complete");
273 Time::delay(100 * Time::Multiplier::Millisecond);
275 for (
size_t i = 0; i < UsbHcd::UhciRootPortCount; i++) {
276 uint16_t nPortStatus = m_pBase->read16(UHCI_PORTSC + (i * 2));
277 NOTICE(
"Port status is " << nPortStatus);
278 if ((!(nPortStatus & 0x80)) && (m_nPorts >= 2)) {
286 if (!UsbHcd::validUhciRootPortCount(m_nPorts)) {
287 panic(
"UHCI detected an unsupported root-port count");
290 for (
size_t i = 0; i < m_nPorts; ++i) {
291 if (!m_PortChanges[i].configure(*
this, 0, i)) {
292 panic(
"UHCI could not configure a root-port publication token");
299 if (!setLegacySupportControl(0x2000) || !pci.updateCommand(
this, 0x400, 5)) {
300 ERROR(
"UHCI: could not enable its PCI interrupt route");
303 m_pBase->write16(0xf, UHCI_INTR);
304 (void)m_pBase->read16(UHCI_INTR);
306 for (
size_t i = 0; i < m_nPorts; ++i) {
311 const size_t portRegister = UHCI_PORTSC + (i * 2);
314 const uint16_t portStatus = m_pBase->read16(portRegister);
315 if (portStatus & UHCI_PORTSC_CSCH) {
316 constexpr uint16_t ChangeMask = UHCI_PORTSC_CSCH | UHCI_PORTSC_EDCH;
317 m_pBase->write16(UsbHcd::selectiveW1cValue(portStatus, ChangeMask,
318 static_cast<uint16_t
>(UHCI_PORTSC_CSCH)),
320 (void)m_pBase->read16(portRegister);
327 const auto observation = m_PortChanges[i].observe();
328 if (!UsbHcd::PortChangeRequest::canAcknowledge(observation.result)) {
329 panic(
"UHCI could not publish its initial root-port state");
331 m_PortChanges[i].acknowledge(observation.generation);
342 m_TimerRegistered = timer->registerHandler(
this);
344 if (!m_TimerRegistered) {
345 ERROR(
"UHCI could not register its root-port polling callback");
349 !timer->
armHandler(
this, Time::getTicks() + Time::Multiplier::Millisecond)) {
350 ERROR(
"UHCI could not arm its root-port polling callback");
351 timer->unregisterHandler(
this);
352 m_TimerRegistered =
false;
356 m_Initialised =
true;
360 QH* pQH =
reinterpret_cast<QH*
>(context);
361 assert(pQH && pQH->pMetaData && pQH->pMetaData->pOwner);
362 Uhci* pOwner = pQH->pMetaData->pOwner;
371 assert(pQH && pQH->pMetaData);
381 QH* pPrev = pQH->pMetaData->pPrev;
382 QH* pNext = pQH->pMetaData->pNext;
383 if (pQH->pMetaData->bPeriodic) {
384 for (
auto it = m_PeriodicSchedule.begin(); it != m_PeriodicSchedule.end(); ++it) {
386 m_PeriodicSchedule.erase(it);
390 pQH->pMetaData->pPeriodicCallback =
nullptr;
392 }
else if (pPrev && pNext) {
393 pPrev->pMetaData->pNext = pNext;
394 pNext->pMetaData->pPrev = pPrev;
395 pPrev->pNext = pQH->pNext;
397 pPrev->bNextInvalid = 0;
402 pQH->pMetaData->pPrev =
nullptr;
403 pQH->pMetaData->pNext =
nullptr;
404 pQH->pMetaData->bIgnore =
true;
408 DoubleWordSet(m_pFrameList, m_pQHListPhys | 2U, 1024);
411 for (
size_t interval = 1; interval <= 128; interval *= 2) {
412 for (
auto it = m_PeriodicSchedule.begin(); it != m_PeriodicSchedule.end(); ++it) {
414 if (qh->pMetaData->periodicInterval != interval)
416 qh->pNext = m_pFrameList[0] >> 4;
418 qh->bNextInvalid = 0;
419 const uint32_t address = m_pQHListPhys + qh->pMetaData->id *
sizeof(
QH);
420 for (
size_t frame = 0; frame < 1024; frame += interval)
421 m_pFrameList[frame] = address | 2U;
427void Uhci::rearmPeriodicCompletion(
void* context) {
428 auto* rearm =
static_cast<PeriodicCompletion*
>(context);
429 Uhci* controller = rearm->controller;
432 if (controller->m_InterruptsClosing || !controller->m_QHBitmap.
test(rearm->transaction))
434 QH* qh = &controller->m_pQHList[rearm->transaction];
435 auto* metadata = qh->pMetaData;
436 if (!metadata || !metadata->pPeriodicCallback ||
437 metadata->periodicGeneration != rearm->generation)
439 const UsbEndpoint& endpoint = metadata->endpointInfo;
440 const size_t port = endpoint.nRootPort;
441 const uint16_t status =
442 port < controller->m_nPorts ? controller->m_pBase->
read16(UHCI_PORTSC + port * 2) : 0;
443 if (port >= controller->m_nPorts || !(status & UHCI_PORTSC_CONN) || (status & UHCI_PORTSC_CSCH) ||
446 if (!(controller->m_pBase->
read16(UHCI_CMD) & UHCI_CMD_RUN) ||
447 (controller->m_pBase->
read16(UHCI_STS) & UHCI_STS_HALT))
452 TD* td = metadata->pFirstTD;
457 td->bDataToggle = !td->bDataToggle;
458 metadata->periodicPending =
false;
459 metadata->nTotalBytes = 0;
462 qh->pElem = (controller->m_pTDListPhys + td->id *
sizeof(TD)) >> 4;
464 qh->bElemInvalid = 0;
469void Uhci::destroyPeriodicCompletion(
void* context) {
470 delete static_cast<PeriodicCompletion*
>(context);
474 assert(pQH && pQH->pMetaData);
476 while (pQH->pMetaData->completedTdList.
count()) {
477 TD* pTD = pQH->pMetaData->completedTdList.
popFront();
478 const size_t id = pTD->id;
479 ByteSet(pTD, 0,
sizeof(
TD));
480 m_TDBitmap.
clear(
id);
483 while (pQH->pMetaData->tdList.
count()) {
485 const size_t id = pTD->id;
486 ByteSet(pTD, 0,
sizeof(
TD));
487 m_TDBitmap.
clear(
id);
490 const size_t id = pQH->pMetaData->id;
491 delete pQH->pMetaData;
492 ByteSet(pQH, 0,
sizeof(
QH));
493 m_QHBitmap.
clear(
id);
503 if (m_TimerRegistered) {
504 if (!
timer || !
timer->unregisterHandler(
this)) {
506 "UHCI teardown could not synchronously unregister its timer "
509 m_TimerRegistered =
false;
514 m_PortChangesClosing =
true;
516 for (
size_t i = 0; i < m_nPorts; ++i) {
535 m_InterruptsClosing =
true;
536 if (m_HardwareOwned) {
537 m_pBase->
write16(0, UHCI_INTR);
538 (void)m_pBase->
read16(UHCI_INTR);
541 panic(
"UHCI teardown could not disable legacy interrupts");
544 m_CallbackOperations.
close();
546 if (m_HardwareOwned) {
551 const uint16_t pending = m_pBase->
read16(UHCI_STS) & 0x1f;
553 m_pBase->
write16(pending, UHCI_STS);
554 (void)m_pBase->
read16(UHCI_STS);
562 assert(pQH && pQH->pMetaData);
564 if (pQH->pMetaData->bPeriodic) {
572 pQH->pMetaData->completion.claimForTeardown(-TransactionError, claim);
577 if (dequeueAdmitted) {
579 {pQH->pMetaData->id, claim.generation}, claim.callback, claim.parameter,
589 if (teardownCompletions.
count())
592 if (m_HardwareOwned) {
593 m_pBase->
write32(0, UHCI_FRLP);
594 (void)m_pBase->
read32(UHCI_FRLP);
603 while (teardownCompletions.
count()) {
604 auto* completion = teardownCompletions.
popFront();
608 if (m_HardwareOwned && !PciBus::instance().updateCommand(
this, 4, 0x400))
609 panic(
"UHCI teardown could not disable PCI DMA and INTx");
612 if (!Machine::instance().getIrqManager()->unregisterHandler(m_IrqId,
615 "UHCI teardown could not synchronously unregister its IRQ "
620 m_CallbackOperations.
wait();
635 size_t periodicCount = 0;
638 while (periodicRetirements.
count()) {
643 while (periodicCount) {
652 if (m_pDequeueThread) {
654 panic(
"UHCI teardown could not join its dequeue worker");
655 m_pDequeueThread =
nullptr;
661 assert(m_CallbackOperations.isClosedAndDrained());
672 constexpr size_t QhListCount = UhciQhRegionBytes /
sizeof(QH);
673 constexpr size_t TdListCount = UhciTdRegionBytes /
sizeof(TD);
677 for (
size_t i = 1; i < QhListCount; ++i) {
678 if (!m_QHBitmap.
test(i))
680 QH* pQH = &m_pQHList[i];
682 panic(
"UHCI teardown found an allocated queue head without metadata");
683 if (pQH->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle)
684 panic(
"UHCI teardown found a live unpublished queue head");
688 for (
size_t i = 1; i < QhListCount; ++i)
690 for (
size_t i = 0; i < TdListCount; ++i)
693 QH* pDummyQH = &m_pQHList[0];
695 delete pDummyQH->pMetaData;
696 ByteSet(pDummyQH, 0,
sizeof(QH));
704void Uhci::doDequeue() {
730 DEBUG_LOG(
"Dequeue complete.");
739 if (!m_CallbackOperations.
tryAcquire(callback)) {
746 if (m_InterruptsClosing) {
750 uint16_t nStatus = m_pBase->
read16(UHCI_STS);
753 return IrqDisposition::NotHandled;
760 m_pBase->
write16(nStatus, UHCI_STS);
761 (void)m_pBase->
read16(UHCI_STS);
764 DEBUG_LOG(
"UHCI IRQ " << nStatus);
772 if (nStatus & (UHCI_STS_INT | UHCI_STS_ERR)) {
773 constexpr size_t QhListCount = UhciQhRegionBytes /
sizeof(
QH);
774 constexpr size_t TdListCount = UhciTdRegionBytes /
sizeof(
TD);
775 size_t qhBudget = QhListCount;
788 bool bPeriodic = pQH->pMetaData->bPeriodic;
789 if (bPeriodic && pQH->pMetaData->periodicPending) {
796 size_t tdBudget = TdListCount;
797 while (pQH->pMetaData->tdList.
count() && tdBudget) {
799 pTD = pQH->pMetaData->tdList.
popFront();
803 if (pTD->bShortTransferTD)
806 bool bEndOfTransfer =
false;
807 if (pTD->nStatus & 0x80) {
814 const UsbEndpoint& endpoint = pQH->pMetaData->endpointInfo;
815 const size_t rootPort = endpoint.nRootPort;
816 const uint16_t rootStatus =
817 rootPort < m_nPorts ? m_pBase->
read16(UHCI_PORTSC + (rootPort * 2)) : 0;
818 if (rootPort < m_nPorts &&
819 (!(rootStatus & UHCI_PORTSC_CONN) || (rootStatus & UHCI_PORTSC_CSCH) ||
825 pQH->pMetaData->bIgnore =
true;
826 pQH->pMetaData->tdList.
pushBack(pTD);
832 if (pTD->nStatus & 0x7e) {
834 ERROR_NOLOCK(
"UHCI TD ERROR!");
835 ERROR_NOLOCK(
"TD Status: " << pTD->nStatus <<
" [" << pTD->nErr
836 <<
"], USB status: " << nStatus);
838 nResult = -pTD->getError();
840 nResult = (pTD->nActLen + 1) % 0x800;
841 pQH->pMetaData->nTotalBytes += nResult;
844 DEBUG_LOG_NOLOCK(
"TD #" <<
Dec << pTD->id <<
" [QH #" << pQH->pMetaData->id <<
Hex
845 <<
"] DONE: " <<
Dec << pTD->nAddress <<
":" << pTD->nEndpoint
847 << (pTD->nPid == UsbPidOut
849 : (pTD->nPid == UsbPidIn
851 : (pTD->nPid == UsbPidSetup ?
"SETUP" :
"")))
852 <<
" " << nResult <<
Hex);
856 bEndOfTransfer = bPeriodic || (nResult < 0) || (pTD == pQH->pMetaData->pLastTD);
859 if ((pTD != pQH->pMetaData->pLastTD) && !bEndOfTransfer) {
861 if (pTD->nEndpoint) {
862 if ((nResult >= 0) && (pTD->nPid == UsbPidIn)) {
869 if (nResult < pTD->nBufferSize) {
870 DEBUG_LOG_NOLOCK(
"UHCI: Short read - got " << nResult <<
" bytes, wanted "
871 << pTD->nBufferSize <<
" bytes");
872 pTD->bShortTransferTD =
true;
873 bEndOfTransfer =
true;
880 pQH->pMetaData->completedTdList.
pushBack(pTD);
884 if (bEndOfTransfer) {
885 const ssize_t completionResult = nResult < 0 ? nResult : pQH->pMetaData->nTotalBytes;
898 assert(claimed && dequeueAdmitted);
899 if (claimed && dequeueAdmitted) {
906 {pQH->pMetaData->id, claim.generation}, claim.callback, claim.parameter,
913 pQH->pMetaData->periodicPending =
true;
918 if (pQH->pMetaData->pPeriodicCallback) {
919 PeriodicCompletion* rearm =
920 nResult >= 0 ?
new PeriodicCompletion{
this, pQH->pMetaData->id,
921 pQH->pMetaData->periodicGeneration}
924 {pQH->pMetaData->id, m_CompletionDeliveries.nextGeneration(),
925 pQH->pMetaData->periodicGeneration},
926 pQH->pMetaData->pPeriodicCallback, pQH->pMetaData->pPeriodicParam,
927 completionResult, rearm ? rearmPeriodicCompletion :
nullptr, rearm,
928 rearm ? destroyPeriodicCompletion :
nullptr, rearm));
936 pQH->pMetaData->tdList.
pushBack(pTD);
941 if (!tdBudget && pQH->pMetaData->tdList.
count()) {
942 ERROR_NOLOCK(
"UHCI: QH #" <<
Dec << pQH->pMetaData->id <<
Hex
943 <<
" exceeded the TD scan budget");
947 if (!pQH->pMetaData->bIgnore)
954 ERROR_NOLOCK(
"UHCI: exceeded the QH scan budget");
959 if (persistList.
count()) {
963 it = persistList.
erase(it);
967 if (completions.
count())
971 while (completions.
count()) {
972 auto* completion = completions.
popFront();
976 return IrqDisposition::Handled;
980 uintptr_t pBuffer,
size_t nBytes) {
987 constexpr size_t QhListCount = UhciQhRegionBytes /
sizeof(
QH);
988 if ((pTransaction ==
static_cast<uintptr_t
>(-1)) || (pTransaction >= QhListCount) || !m_pQHList ||
989 !m_QHBitmap.
test(pTransaction) || !m_pQHList[pTransaction].pMetaData) {
990 ERROR(
"USB: UHCI: invalid transaction for transfer");
993 QH* pQH = &m_pQHList[pTransaction];
994 if (pQH->pMetaData->bBuildFailed || pQH->pMetaData->pPrev || pQH->pMetaData->pNext ||
995 (!pQH->pMetaData->bPeriodic &&
996 pQH->pMetaData->
completion.state() != UsbHcd::TransferCompletion::State::Idle)) {
997 ERROR(
"USB: UHCI: cannot extend an accepted transaction");
1000 if (nBytes > UhciMaximumTransferBytes) {
1001 ERROR(
"USB: UHCI: transfer exceeds one TD");
1002 pQH->pMetaData->bBuildFailed =
true;
1006 if (nIndex >= (UhciTdRegionBytes /
sizeof(
TD))) {
1007 ERROR(
"USB: UHCI: TD space full");
1008 pQH->pMetaData->bBuildFailed =
true;
1011 m_TDBitmap.
set(nIndex);
1014 TD* pTD = &m_pTDList[nIndex];
1015 ByteSet(pTD, 0,
sizeof(
TD));
1016 pTD->bNextInvalid = 1;
1020 pTD->nStatus = 0x80;
1029 pTD->bLoSpeed = pQH->pMetaData->endpointInfo.speed == LowSpeed;
1032 pTD->bSpd = pid == UsbPidIn && pQH->pMetaData->endpointInfo.nEndpoint;
1035 pTD->nAddress = pQH->pMetaData->endpointInfo.nAddress;
1036 pTD->nEndpoint = pQH->pMetaData->endpointInfo.nEndpoint;
1037 pTD->bDataToggle = bToggle;
1040 pTD->nMaxLen = nBytes ? nBytes - 1 : 0x7ff;
1041 pTD->nBufferSize = nBytes;
1044 physical_uintptr_t physicalBuffer = 0;
1045 if (!DriverDma::contiguousVirtualRangeToPhysical(va, pBuffer, nBytes, physicalBuffer) ||
1046 !DriverDma::physicalRangeFits(physicalBuffer, nBytes, UhciHighestDmaAddress)) {
1047 ERROR(
"UHCI: addTransferToTransaction: buffer range is not contiguous DMA memory");
1048 pQH->pMetaData->bBuildFailed =
true;
1049 ByteSet(pTD, 0,
sizeof(
TD));
1050 m_TDBitmap.
clear(nIndex);
1053 pTD->pBuff = physicalBuffer;
1057 if (pQH->pMetaData->pLastTD) {
1058 pQH->pMetaData->pLastTD->pNext = PHYS_TD(nIndex) >> 4;
1059 pQH->pMetaData->pLastTD->bNextInvalid = 0;
1060 pQH->pMetaData->pLastTD->bNextQH = 0;
1063 pQH->pMetaData->pFirstTD = pTD;
1064 pQH->pElem = PHYS_TD(nIndex) >> 4;
1065 pQH->bElemInvalid = 0;
1068 pQH->pMetaData->pLastTD = pTD;
1070 pQH->pMetaData->tdList.
pushBack(pTD);
1076 return static_cast<uintptr_t
>(-1);
1081 return static_cast<uintptr_t
>(-1);
1083 if (nIndex >= (UhciQhRegionBytes /
sizeof(
QH))) {
1084 ERROR(
"USB: UHCI: QH space full");
1085 return static_cast<uintptr_t
>(-1);
1087 m_QHBitmap.
set(nIndex);
1090 QH* pQH = &m_pQHList[nIndex];
1091 ByteSet(pQH, 0,
sizeof(
QH));
1096 pQH->pMetaData->pOwner =
this;
1097 pQH->pMetaData->pPeriodicCallback =
nullptr;
1098 pQH->pMetaData->pPeriodicParam = 0;
1099 pQH->pMetaData->periodicGeneration = 0;
1100 pQH->pMetaData->periodicInterval = 0;
1101 pQH->pMetaData->periodicBusTime = 0;
1102 pQH->pMetaData->periodicPending =
false;
1103 pQH->pMetaData->endpointInfo = endpointInfo;
1104 pQH->pMetaData->bPeriodic =
false;
1105 pQH->pMetaData->bBuildFailed =
false;
1106 pQH->pMetaData->pFirstTD = pQH->pMetaData->pLastTD = 0;
1107 pQH->pMetaData->nTotalBytes = 0;
1108 pQH->pMetaData->pPrev = pQH->pMetaData->pNext = 0;
1109 pQH->pMetaData->bIgnore =
false;
1110 pQH->pMetaData->id = nIndex;
1115bool Uhci::doAsync(uintptr_t pTransaction,
void (*pCallback)(uintptr_t, ssize_t),
1117 return doAsyncOwned(pTransaction, pCallback, pParam,
nullptr, 0);
1120bool Uhci::doAsyncOwned(uintptr_t pTransaction,
void (*pCallback)(uintptr_t, ssize_t),
1122 size_t interruptGeneration) {
1128 constexpr size_t QhListCount = UhciQhRegionBytes /
sizeof(QH);
1129 if ((pTransaction ==
static_cast<uintptr_t
>(-1)) || (pTransaction >= QhListCount) ||
1130 !m_QHBitmap.
test(pTransaction)) {
1131 ERROR(
"UHCI: doAsync: didn't get a valid transaction id [" << pTransaction <<
"].");
1135 QH* pQH = &m_pQHList[pTransaction];
1136 if (!pQH->pMetaData) {
1137 ByteSet(pQH, 0,
sizeof(QH));
1138 m_QHBitmap.
clear(pTransaction);
1141 if (pQH->pMetaData->bBuildFailed || !pQH->pMetaData->pLastTD) {
1142 ERROR(
"UHCI: doAsync: transaction could not be submitted [" << pTransaction <<
"].");
1146 if (pQH->pMetaData->pPrev || pQH->pMetaData->pNext ||
1147 (!pQH->pMetaData->bPeriodic &&
1148 pQH->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle)) {
1149 ERROR(
"UHCI: doAsync: transaction is already owned [" << pTransaction <<
"].");
1161 ERROR(
"UHCI: Queue tail is null!");
1166 if (pQH->pMetaData->bPeriodic) {
1167 size_t busTime = pQH->pMetaData->periodicBusTime;
1169 for (
auto it = m_PeriodicSchedule.begin(); it != m_PeriodicSchedule.end(); ++it)
1170 busTime += (*it)->pMetaData->periodicBusTime;
1171 if (busTime > 900) {
1172 ERROR(
"UHCI: periodic frame budget exhausted");
1184 pQH->pMetaData->bIgnore =
true;
1185 if (pQH->pMetaData->bPeriodic) {
1186 pQH->pMetaData->pPrev = pQH->pMetaData->pNext = pQH;
1187 m_PeriodicSchedule.pushBack(pQH);
1191 reinterpret_cast<uintptr_t
>(m_pQHList)) /
1193 pQH->pNext = (m_pQHListPhys + (queueHeadIndex *
sizeof(QH))) >> 4;
1194 pQH->bNextInvalid = 0;
1198 pOldTail->pNext = (m_pQHListPhys + (pTransaction *
sizeof(QH))) >> 4;
1199 pOldTail->bNextInvalid = 0;
1200 pOldTail->bNextQH = 1;
1201 pOldTail->pMetaData->pNext = pQH;
1203 pQH->pMetaData->pPrev = pOldTail;
1208 pQH->pMetaData->pLastTD->bIoc = 1;
1209 if (pQH->pMetaData->bPeriodic) {
1210 pQH->pMetaData->pPeriodicCallback = pCallback;
1211 pQH->pMetaData->pPeriodicParam = pParam;
1212 if (!interruptHandle || pQH->pMetaData->periodicGeneration != interruptGeneration ||
1214 pCallback, pParam)) {
1215 panic(
"UHCI could not publish interrupt-IN ownership before DMA");
1223 pQH->pMetaData->bIgnore =
false;
1237 bool drainDelivery =
false;
1242 constexpr size_t QhListCount = UhciQhRegionBytes /
sizeof(
QH);
1243 if ((pTransaction !=
static_cast<uintptr_t
>(-1)) && (pTransaction < QhListCount) &&
1244 m_QHBitmap.
test(pTransaction)) {
1245 QH* pQH = &m_pQHList[pTransaction];
1246 if (pQH->pMetaData && !pQH->pMetaData->bPeriodic) {
1250 pCallback, pParam, -TransactionError, claim);
1252 if (disposition == UsbHcd::TransferCompletion::CancellationDisposition::Claimed) {
1253 const uint16_t interruptMask = m_pBase->
read16(UHCI_INTR);
1254 m_pBase->
write16(0, UHCI_INTR);
1255 (void)m_pBase->
read16(UHCI_INTR);
1260 if (dequeueAdmitted) {
1267 {pQH->pMetaData->id, claim.generation}, claim.callback, claim.parameter,
1272 if (!m_InterruptsClosing) {
1274 m_pBase->
write16(interruptMask, UHCI_INTR);
1276 m_pBase->
write16(0, UHCI_INTR);
1278 (void)m_pBase->
read16(UHCI_INTR);
1279 }
else if (disposition ==
1280 UsbHcd::TransferCompletion::CancellationDisposition::DrainPublished) {
1281 deliveryKey = {pTransaction, claim.generation};
1282 drainDelivery =
true;
1288 while (completions.
count()) {
1289 auto* completion = completions.
popFront();
1298 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
1299 bool producerAlreadyStopped) {
1302 panic(
"UHCI interrupt-IN cancellation raced controller teardown");
1304 if (!producerAlreadyStopped) {
1305 bool matched =
false;
1308 constexpr size_t QhListCount = UhciQhRegionBytes /
sizeof(
QH);
1309 if (token.transaction < QhListCount && m_QHBitmap.
test(token.transaction)) {
1310 QH* pQH = &m_pQHList[token.transaction];
1312 if (metadata && metadata->bPeriodic && metadata->periodicGeneration == token.generation &&
1313 metadata->pPeriodicCallback == callback && metadata->pPeriodicParam == parameter) {
1315 const uint16_t interruptMask = m_pBase->
read16(UHCI_INTR);
1316 m_pBase->
write16(0, UHCI_INTR);
1317 (void)m_pBase->
read16(UHCI_INTR);
1324 metadata->pPeriodicCallback =
nullptr;
1325 metadata->pPeriodicParam = 0;
1330 if (!m_InterruptsClosing) {
1332 m_pBase->
write16(interruptMask, UHCI_INTR);
1334 (void)m_pBase->
read16(UHCI_INTR);
1340 panic(
"UHCI interrupt-IN handle lost its controller subscription");
1347 void (*pCallback)(uintptr_t, ssize_t),
1352 if (!pBuffer || !nBytes || !endpointInfo.nEndpoint || !endpointInfo.nInterval ||
1353 nBytes > endpointInfo.nMaxPacketSize ||
1354 (endpointInfo.speed != LowSpeed && endpointInfo.speed != FullSpeed) ||
1355 endpointInfo.nMaxPacketSize > (endpointInfo.speed == LowSpeed ? 8U : 64U))
1360 if (nTransaction ==
static_cast<uintptr_t
>(-1)) {
1361 ERROR(
"USB: UHCI: Couldn't create interrupt transaction!");
1366 QH* pQH = &m_pQHList[nTransaction];
1370 pQH->pMetaData->bPeriodic =
true;
1371 pQH->pMetaData->periodicGeneration = generation;
1372 size_t interval = 1;
1373 while (interval * 2 <= endpointInfo.nInterval)
1375 pQH->pMetaData->periodicInterval = interval;
1378 pQH->pMetaData->periodicBusTime =
1379 (nBytes + 32U) * (endpointInfo.speed == LowSpeed ? 7U : 1U) + 20U;
1384 if (!pQH->pMetaData->pLastTD) {
1385 ERROR(
"USB: UHCI: Couldn't add transfer to transaction!");
1387 if (m_QHBitmap.
test(nTransaction) && pQH->pMetaData)
1393 if (!doAsyncOwned(nTransaction, pCallback, pParam, &handle, generation)) {
1394 ERROR(
"USB: UHCI: Couldn't submit interrupt transaction!");
1396 if (m_QHBitmap.
test(nTransaction) && pQH->pMetaData)
1405 constexpr uint16_t ChangeMask = UHCI_PORTSC_CSCH | UHCI_PORTSC_EDCH;
1407 uint16_t portControl = UsbHcd::selectiveW1cValue(m_pBase->
read16(portRegister), ChangeMask,
1408 static_cast<uint16_t
>(0));
1409 portControl &= ~clearMask;
1410 portControl |= setMask & ~ChangeMask;
1411 m_pBase->
write16(portControl, portRegister);
1416 DEBUG_LOG(
"USB: UHCI: Reset on port " << nPort);
1419 const size_t portRegister = UHCI_PORTSC + (nPort * 2);
1420 constexpr uint16_t ChangeMask = UHCI_PORTSC_CSCH | UHCI_PORTSC_EDCH;
1422 if (bErrorResponse) {
1425 constexpr size_t PortDisablePollLimit = 100;
1426 size_t disablePolls = PortDisablePollLimit;
1427 while (disablePolls-- && (m_pBase->
read16(portRegister) & UHCI_PORTSC_ENABLE)) {
1428 Time::delay(1 * Time::Multiplier::Millisecond);
1430 if (m_pBase->
read16(portRegister) & UHCI_PORTSC_ENABLE) {
1431 ERROR(
"USB: UHCI: Port " <<
Dec << nPort <<
Hex <<
" did not disable within 100 ms");
1438 Time::delay(50 * Time::Multiplier::Millisecond);
1443 Time::delay((bErrorResponse ? 500 : 100) * Time::Multiplier::Millisecond);
1446 if (!(m_pBase->
read16(portRegister) & UHCI_PORTSC_ENABLE)) {
1448 DEBUG_LOG(
"USB: UHCI: During reset, port "
1449 << nPort <<
" could not be enabled. It may become enabled soon.");
1458 const uint16_t portStatus = m_pBase->
read16(portRegister);
1459 if (portStatus & UHCI_PORTSC_EDCH) {
1460 m_pBase->
write16(UsbHcd::selectiveW1cValue(portStatus, ChangeMask,
1461 static_cast<uint16_t
>(UHCI_PORTSC_EDCH)),
1463 (void)m_pBase->
read16(portRegister);
1468 if (!(m_pBase->
read16(portRegister) & UHCI_PORTSC_CONN)) {
1470 DEBUG_LOG(
"USB: UHCI: During reset, port "
1472 <<
" was enabled but had no device on it. A device may be detected "
1479 DEBUG_LOG(
"USB: Post-reset status is " << m_pBase->
read16(portRegister));
1486 uint64_t p6, uint64_t p7, uint64_t p8) {
1487 if (p1 >= m_nPorts) {
1495 const uint16_t portStatus = m_pBase->
read16(UHCI_PORTSC + (p1 * 2));
1498 if (portStatus & UHCI_PORTSC_CONN) {
1500 if (portStatus & UHCI_PORTSC_LOSPEED) {
1501 DEBUG_LOG(
"USB: UHCI [" <<
this <<
"]: Port " <<
Dec << p1 <<
Hex
1502 <<
" has a low-speed device connected to it");
1504 WARNING(
"USB: UHCI [" <<
this <<
"]: Port " <<
Dec << p1 <<
Hex
1505 <<
" appeared to be connected but could not be set up");
1507 DEBUG_LOG(
"USB: UHCI [" <<
this <<
"]: Port " <<
Dec << p1 <<
Hex
1508 <<
" has a full-speed device connected to it");
1510 WARNING(
"USB UHCI [" <<
this <<
"]: Port " <<
Dec << p1 <<
Hex
1511 <<
" appeared to be connected but could not be set up");
1514 DEBUG_LOG(
"USB: UHCI [" <<
this <<
"]: Device on port " <<
Dec << p1 <<
Hex
1515 <<
" disconnected.");
1522 if (request.p1 < m_nPorts) {
1523 m_PortChanges[request.p1].cancel(request.p8);
1533 if (!m_CallbackOperations.
tryAcquire(callback)) {
1537 uint64_t nextPoll = Time::Multiplier::Millisecond;
1540 if (m_PortChangesClosing) {
1552 for (
size_t i = 0; i < m_nPorts; i++) {
1553 const size_t portRegister = UHCI_PORTSC + (i * 2);
1554 const uint16_t portStatus = m_pBase->
read16(portRegister);
1555 constexpr uint16_t ChangeMask = UHCI_PORTSC_CSCH | UHCI_PORTSC_EDCH;
1556 uint16_t acknowledgeMask = portStatus & UHCI_PORTSC_EDCH;
1557 size_t acknowledgeGeneration = 0;
1559 if (portStatus & UHCI_PORTSC_CSCH) {
1561 acknowledgeMask |= UHCI_PORTSC_CSCH;
1563 const auto observation = m_PortChanges[i].observe();
1564 if (UsbHcd::PortChangeRequest::canAcknowledge(observation.result)) {
1565 acknowledgeMask |= UHCI_PORTSC_CSCH;
1566 acknowledgeGeneration = observation.generation;
1571 if (acknowledgeMask) {
1572 m_pBase->
write16(UsbHcd::selectiveW1cValue(portStatus, ChangeMask, acknowledgeMask),
1574 (void)m_pBase->
read16(portRegister);
1576 if (acknowledgeGeneration) {
1577 m_PortChanges[i].acknowledge(acknowledgeGeneration);
1584 !source->
armHandler(
this, Time::getTicks() + nextPoll)) {
1585 panic(
"UHCI could not rearm its root-port polling callback");
1592 if (port >= m_nPorts) {
1593 ERROR(
"UHCI: invalid suppressed root-port replay " <<
Dec << port);
1599 if (m_PortChangesClosing) {
1602 const auto observation = m_PortChanges[port].observe();
1603 const bool accepted = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
1605 m_PortChanges[port].acknowledge(observation.generation);
1609 m_PortChangesClosing =
true;
1610 ERROR(
"UHCI: live suppressed root-port replay could not be published");
1618 return port < m_nPorts ? m_PortChanges[port].observedGeneration() : 0;
1623 constexpr size_t TransitionPollLimit = 100;
1624 size_t polls = TransitionPollLimit;
1625 while (polls-- && !(m_pBase->
read16(UHCI_STS) & UHCI_STS_HALT))
1626 Time::delay(1 * Time::Multiplier::Millisecond);
1627 if (!(m_pBase->
read16(UHCI_STS) & UHCI_STS_HALT))
1628 panic(
"UHCI controller did not halt within 100 ms");
1633 constexpr size_t TransitionPollLimit = 100;
1634 size_t polls = TransitionPollLimit;
1635 while (polls-- && (m_pBase->
read16(UHCI_STS) & UHCI_STS_HALT))
1636 Time::delay(1 * Time::Multiplier::Millisecond);
1637 if (m_pBase->
read16(UHCI_STS) & UHCI_STS_HALT)
1638 panic(
"UHCI controller did not start within 100 ms");
1644 constexpr uint16_t Enables = 0x20bf;
1645 uint16_t actual = 0;
1646 auto& pci = PciBus::instance();
1647 return pci.writeConfig16(
this, 0xc0, control) && pci.readConfig16(
this, 0xc0, actual) &&
1648 (actual & Enables) == (control & Enables);
void map(size_t forcedSize=0, bool bUser=false, bool bWriteCombine=false, bool bWriteThrough=false)
bool test(size_t n) const
virtual void write32(uint32_t value, size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
virtual void write16(uint16_t value, size_t offset=0)=0
virtual uint16_t read16(size_t offset=0)=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)
::Iterator< T, node_t > Iterator
virtual Timer * getTimer()=0
MUST_USE_RESULT bool tryAcquire(Lease &lease)
MUST_USE_RESULT bool tryEnter()
static const size_t continuous
static PhysicalMemoryManager & instance()
static const size_t below4GB
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
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
virtual bool supportsDeadlines() const
virtual bool armHandler(TimerHandler *, uint64_t)
static void enqueueCompletedTransfer(void *context)
Mutex m_IrqProcessingLock
IrqDisposition irq(irq_id_t number) override
IRQ handler.
size_t currentRootPortGeneration(size_t port) const override
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)
QH * m_pCurrentAsyncQueueHead
void replaySuppressedConnectionChange(size_t port) override
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
bool setLegacySupportControl(uint16_t control)
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
List< QH * > m_AsyncSchedule
List of QHs in the active asynchronous schedule.
Semaphore m_DequeueCount
Semaphore for the dequeue list.
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
OperationBarrier m_SubmissionOperations
void cancelRequest(const Request &request) override
void rebuildPeriodicScheduleLocked()
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 detachQueueHeadLocked(QH *pQH)
void stop()
Stops the controller or panics before DMA ownership can be transferred.
void modifyPortControl(size_t portRegister, uint16_t clearMask, uint16_t setMask)
Serialises control RMWs with scanning without echoing W1C status bits.
MUST_USE_RESULT bool cancelInterruptInAndDrain(const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter, bool producerAlreadyStopped) override
List< QH * > m_DequeueList
List of QHs ready for dequeue.
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
uint64_t m_nPortCheckTicks
The time passed since last port check.
void timer(uint64_t delta)
Timer callback to handle port status changes.
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)
Adds a new transfer to an existent transaction.
UsbHcd::CallbackDeliveryQueue m_CompletionDeliveries
QH * m_pCurrentAsyncQueueTail
OperationBarrier m_DequeueOperations
OperationBarrier m_TransferOperations
void reclaimQueueHeadLocked(QH *pQH)
OperationBarrier m_CancelOperations
void start()
Starts the controller or panics instead of leaving work silently stuck.
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)
MUST_USE_RESULT bool captureNatural(ssize_t result)
MUST_USE_RESULT bool claimCaptured(Claim &claim)
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)
MUST_USE_RESULT bool deferConnectionChangeIfSuppressed(size_t port)
void disconnectAllDevices()
static const size_t KernelMode
static const size_t Write
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Iterator erase(Iterator &Iter)
void pushFront(const T &value)
void pushBack(const T &value)