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/types.h"
29#include "pedigree/kernel/panic.h"
30#include "pedigree/kernel/process/Mutex.h"
31#include "pedigree/kernel/processor/IoBase.h"
32#include "pedigree/kernel/processor/MemoryRegion.h"
33#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
34#include "pedigree/kernel/processor/Processor.h"
35#include "pedigree/kernel/processor/ProcessorInformation.h"
36#include "pedigree/kernel/processor/VirtualAddressSpace.h"
37#include "pedigree/kernel/processor/types.h"
38#include "pedigree/kernel/time/Time.h"
39#include "pedigree/kernel/utilities/ExtensibleBitmap.h"
40#include "pedigree/kernel/utilities/Iterator.h"
41#include "pedigree/kernel/utilities/List.h"
42#include "pedigree/kernel/utilities/RequestQueue.h"
43#include "pedigree/kernel/utilities/String.h"
44#include "pedigree/kernel/utilities/Vector.h"
45#include "pedigree/kernel/utilities/utility.h"
47#include "modules/drivers/common/DmaBuffer.h"
48#include "modules/system/usb/Usb.h"
49#include "modules/system/usb/UsbHub.h"
52constexpr size_t OhciHardwarePageBytes = 4096;
53constexpr size_t OhciDescriptorRegionBytes = 4096;
54constexpr size_t OhciDescriptorOffsetMask = OhciDescriptorRegionBytes - 1;
55constexpr size_t OhciHccaOffset = 0;
56constexpr size_t OhciBulkEdOffset = OhciDescriptorRegionBytes;
57constexpr size_t OhciControlEdOffset = 2 * OhciDescriptorRegionBytes;
58constexpr size_t OhciPeriodicEdOffset = 3 * OhciDescriptorRegionBytes;
59constexpr size_t OhciTdOffset = 4 * OhciDescriptorRegionBytes;
60constexpr size_t OhciDmaRegionBytes = 5 * OhciDescriptorRegionBytes;
61constexpr size_t OhciBufferPageCount = 2;
62constexpr physical_uintptr_t OhciHighestDmaAddress = 0xffffffff;
65#define INDEX_FROM_TD(ptr) \
66 (((reinterpret_cast<uintptr_t>((ptr)) & OhciDescriptorOffsetMask) / sizeof(TD)))
67#define PHYS_TD(idx) (m_pTDListPhys + ((idx) * sizeof(TD)))
70struct OhciCompletionCleanup {
77void Ohci::finishDeferredCompletion(
void* context) {
78 auto* cleanup =
reinterpret_cast<OhciCompletionCleanup*
>(context);
79 Ohci* controller = cleanup->controller;
83 if (ed->pMetaData && ed->pMetaData->completion.generation() == cleanup->generation) {
86 panic(
"OHCI completion cleanup lost its transaction generation");
101 m_IrqProcessingLock(),
102 m_CompletionDeliveries(),
104 m_RootHubStatusChangeDesired(false),
105 m_PortResetActive(false),
107 m_ScheduleChangeLock(),
108 m_PeriodicListChangeLock(),
109 m_ControlListChangeLock(),
110 m_BulkListChangeLock(),
111 m_pPeriodicEDList(nullptr),
112 m_pPeriodicEDListPhys(0),
113 m_PeriodicEDBitmap(),
114 m_pControlEDList(nullptr),
115 m_pControlEDListPhys(0),
117 m_pBulkEDList(nullptr),
118 m_pBulkEDListPhys(0),
123 m_pBulkQueueHead(nullptr),
124 m_pControlQueueHead(nullptr),
125 m_pBulkQueueTail(nullptr),
126 m_pControlQueueTail(nullptr),
127 m_pPeriodicQueueTail(nullptr),
132 m_CallbackOperations(),
133 m_SubmissionOperations(),
134 m_CancellationOperations(),
135 m_AcceptedOperations(),
137 setSpecificType(
String(
"OHCI"));
142 ERROR(
"OHCI requires threaded PCI IRQ delivery");
148 ERROR(
"OHCI: target pages cannot represent 4 KiB OHCI buffer pages");
154 m_OhciMR, DriverDma::pageCountForBytes(OhciDmaRegionBytes),
157 ERROR(
"USB: OHCI: Couldn't allocate memory region!");
161 uintptr_t virtualBase =
reinterpret_cast<uintptr_t
>(m_OhciMR.virtualAddress());
162 uintptr_t physicalBase = m_OhciMR.physicalAddress();
164 m_pHcca =
reinterpret_cast<Hcca*
>(virtualBase + OhciHccaOffset);
165 m_pBulkEDList =
reinterpret_cast<ED*
>(virtualBase + OhciBulkEdOffset);
166 m_pControlEDList =
reinterpret_cast<ED*
>(virtualBase + OhciControlEdOffset);
167 m_pPeriodicEDList =
reinterpret_cast<ED*
>(virtualBase + OhciPeriodicEdOffset);
168 m_pTDList =
reinterpret_cast<TD*
>(virtualBase + OhciTdOffset);
170 m_pHccaPhys = physicalBase + OhciHccaOffset;
171 m_pBulkEDListPhys = physicalBase + OhciBulkEdOffset;
172 m_pControlEDListPhys = physicalBase + OhciControlEdOffset;
173 m_pPeriodicEDListPhys = physicalBase + OhciPeriodicEdOffset;
174 m_pTDListPhys = physicalBase + OhciTdOffset;
177 ByteSet(m_pHcca, 0, 0x800);
180 m_PeriodicEDBitmap.set(0);
181 ED* pPeriodicED = m_pPeriodicEDList;
182 ByteSet(pPeriodicED, 0,
sizeof(ED));
183 pPeriodicED->bSkip =
true;
184 pPeriodicED->pMetaData =
new ED::MetaData;
185 pPeriodicED->pMetaData->pCallback =
nullptr;
186 pPeriodicED->pMetaData->pParam = 0;
187 pPeriodicED->pMetaData->bPeriodic =
true;
188 pPeriodicED->pMetaData->bBuildFailed =
false;
189 pPeriodicED->pMetaData->pFirstTD =
nullptr;
190 pPeriodicED->pMetaData->pLastTD =
nullptr;
191 pPeriodicED->pMetaData->nTotalBytes = 0;
192 pPeriodicED->pMetaData->bIgnore =
true;
193 pPeriodicED->pMetaData->bLinked =
false;
194 pPeriodicED->pMetaData->edType = PeriodicList;
195 pPeriodicED->pMetaData->acceptedOperation =
false;
196 pPeriodicED->pMetaData->id = 2 * OhciDescriptorRegionBytes;
197 pPeriodicED->pMetaData->pPrev = pPeriodicED->pMetaData->pNext = pPeriodicED;
200 DoubleWordSet(m_pHcca->pInterruptEDList, m_pPeriodicEDListPhys, 3);
203 m_pPeriodicQueueTail = pPeriodicED;
205 m_pBulkQueueTail = m_pBulkQueueHead = 0;
206 m_pControlQueueTail = m_pControlQueueHead = 0;
215 m_pBase = m_Addresses[0]->m_Io;
216 m_Addresses[0]->map();
219 uint8_t version = m_pBase->read32(OhciVersion) & 0xFF;
220 DEBUG_LOG(
"USB: OHCI: starting up - controller is version "
221 <<
Dec << ((version & 0xF0) >> 4) <<
"." << (version & 0xF) <<
Hex <<
".");
225 m_pBase->write32(OhciInterruptAll, OhciInterruptDisable);
226 (void)m_pBase->read32(OhciInterruptEnable);
229 uint32_t control = m_pBase->read32(OhciControl);
230 if (control & OhciControlInterruptRoute) {
232 DEBUG_LOG(
"USB: OHCI: currently in SMM!");
233 uint32_t status = m_pBase->read32(OhciCommandStatus);
234 m_pBase->write32(status | OhciCommandRequestOwnership, OhciCommandStatus);
235 constexpr size_t OwnershipPollLimit = 1000;
236 size_t ownershipPolls = OwnershipPollLimit;
237 while ((control = m_pBase->read32(OhciControl)) & OhciControlInterruptRoute) {
238 if (!ownershipPolls--)
239 panic(
"OHCI firmware ownership handoff timed out after 1 s");
240 Time::delay(1 * Time::Multiplier::Millisecond);
244 if (control & OhciControlStateFunctionalMask) {
245 DEBUG_LOG(
"USB: OHCI: BIOS is currently in charge.");
247 DEBUG_LOG(
"USB: OHCI: not yet operational.");
251 if (!(control & OhciControlStateRunning))
252 m_pBase->write32(OhciControlStateRunning, OhciControl);
256 m_pBase->write32(control & ~OhciControlStateFunctionalMask, OhciControl);
257 Time::delay(200 * Time::Multiplier::Millisecond);
260 uint32_t interval = m_pBase->read32(OhciFmInterval);
263 m_pBase->write32(OhciCommandHcReset, OhciCommandStatus);
264 constexpr size_t ResetPollLimit = 20;
265 size_t resetPolls = ResetPollLimit;
266 while (m_pBase->read32(OhciCommandStatus) & OhciCommandHcReset) {
268 panic(
"OHCI controller reset timed out after 100 ms");
269 Time::delay(5 * Time::Multiplier::Millisecond);
276 m_pBase->write32(m_pHccaPhys, OhciHcca);
279 m_pBase->write32(m_pControlEDListPhys, OhciControlHeadED);
280 m_pBase->write32(m_pBulkEDListPhys, OhciBulkHeadED);
284 m_pBase->write32(OhciInterruptAll, OhciInterruptDisable);
285 (void)m_pBase->read32(OhciInterruptEnable);
286 m_pBase->write32(OhciInterruptOwnershipChange | 0x7F, OhciInterruptStatus);
287 (void)m_pBase->read32(OhciInterruptStatus);
290 control = m_pBase->read32(OhciControl);
291 control &= ~(0x3 | 0x3C | OhciControlStateFunctionalMask |
292 OhciControlInterruptRoute);
293 control |= OhciControlListsEnable | OhciControlStateRunning | 0x3;
294 m_pBase->write32(control, OhciControl);
299 m_pBase->write32(interval | (1U << 31U), OhciFmInterval);
301 DEBUG_LOG(
"USB: OHCI: maximum packet size is " << ((interval >> 16) & 0xEFFF));
304 m_pBase->write32(OhciRhHubStsSetGlobalPower, OhciRhStatus);
310 if (getLifecycleState() != RequestQueue::LifecycleState::Accepting) {
311 ERROR(
"OHCI: request queue did not enter the accepting state");
323 static_cast<IrqHandler*
>(
this),
this, IrqPolicy::pciIntxThreaded());
325 ERROR(
"OHCI: could not register the PCI interrupt callback");
328 Machine::instance().getIrqManager()->
control(
329 getInterruptNumber(), IrqManager::MitigationThreshold,
335 uint32_t rhDescA = m_pBase->read32(OhciRhDescriptorA);
336 uint8_t powerWait = ((rhDescA >> 24) & 0xFF) * 2;
337 m_nPorts = rhDescA & 0xFF;
339 if (!UsbHcd::validOhciRootPortCount(m_nPorts)) {
340 ERROR(
"OHCI: unsupported root-port count " <<
Dec << m_nPorts <<
Hex);
345 for (
size_t i = 0; i < m_nPorts; ++i) {
346 if (!m_PortChanges[i].configure(*
this, 0, i)) {
347 ERROR(
"OHCI: could not configure root-port publication " << i);
352 DEBUG_LOG(
"USB: OHCI: Reset complete, " <<
Dec << m_nPorts <<
Hex <<
" ports available");
359 m_pBase->write32(OhciInterruptRhStsChange, OhciInterruptStatus);
360 (void)m_pBase->read32(OhciInterruptStatus);
362 if (m_pBase->read32(OhciRhStatus) & OhciRhHubStsOverCurrentCh) {
363 m_pBase->write32(OhciRhHubStsOverCurrentCh, OhciRhStatus);
364 (void)m_pBase->read32(OhciRhStatus);
369 for (
size_t i = 0; i < m_nPorts; ++i) {
370 const size_t portRegister = OhciRhPortStatus + (i * 4);
371 const uint32_t portChanges = m_pBase->read32(portRegister) & OhciRhPortStsChangeMask;
373 m_pBase->write32(portChanges, portRegister);
374 (void)m_pBase->read32(portRegister);
381 m_pBase->write32(OhciInterruptOperational, OhciInterruptEnable);
382 (void)m_pBase->read32(OhciInterruptEnable);
384 for (
size_t i = 0; i < m_nPorts; i++) {
385 if (!(m_pBase->read32(OhciRhPortStatus + (i * 4)) & OhciRhPortStsPower)) {
386 DEBUG_LOG(
"USB: OHCI: applying power to port " << i);
389 m_pBase->write32(OhciRhPortStsPower, OhciRhPortStatus + (i * 4));
392 Time::delay(powerWait * Time::Multiplier::Millisecond);
395 DEBUG_LOG(
"OHCI: Determining if there's a device on this port");
398 if (m_pBase->read32(OhciRhPortStatus + (i * 4)) & OhciRhPortStsConnected) {
402 const auto observation = m_PortChanges[i].observe();
403 if (!UsbHcd::PortChangeRequest::canAcknowledge(observation.result)) {
404 panic(
"OHCI could not publish its initial root-port state");
406 m_PortChanges[i].acknowledge(observation.generation);
416 m_RootHubStatusChangeDesired =
true;
417 setRootHubStatusChangeSource(
true);
421 m_Initialised =
true;
431 m_RootHubStatusChangeDesired =
false;
436 for (
size_t i = 0; i < m_nPorts; ++i) {
451 uint32_t resetControl = 0;
453 if (m_pBase && m_pHcca) {
458 const uint32_t control = m_pBase->
read32(OhciControl);
459 resetControl = control & ~(OhciControlStateFunctionalMask | 0x3C);
460 m_pBase->
write32(OhciInterruptMIE, OhciInterruptDisable);
462 "OHCI teardown suspend timed out after 100 ms");
483 constexpr size_t EdCount = OhciDescriptorRegionBytes /
sizeof(ED);
484 for (
size_t i = 0; i < EdCount; ++i) {
485 if (m_ControlEDBitmap.
test(i))
487 if (m_BulkEDBitmap.
test(i))
495 ED* pPeriodicDummy = m_pPeriodicEDList;
498 if (pPeriodicDummy) {
499 pPeriodicDummy->pNext = 0;
500 if (pPeriodicDummy->pMetaData) {
501 pPeriodicDummy->pMetaData->pPrev = pPeriodicDummy;
502 pPeriodicDummy->pMetaData->pNext = pPeriodicDummy;
504 m_pPeriodicQueueTail = pPeriodicDummy;
507 for (
size_t i = 1; i < EdCount; ++i) {
508 if (!m_PeriodicEDBitmap.
test(i))
511 ED* pED = &m_pPeriodicEDList[i];
514 if (pED->pMetaData) {
515 pED->pMetaData->bIgnore =
true;
516 pED->pMetaData->bLinked =
false;
517 pED->pMetaData->pPrev =
nullptr;
518 pED->pMetaData->pNext =
nullptr;
524 if (completions.
count())
527 m_pBase->
write32(OhciInterruptAll, OhciInterruptDisable);
528 (void)m_pBase->
read32(OhciInterruptEnable);
529 m_pBase->
write32(OhciInterruptOwnershipChange | 0x7F, OhciInterruptStatus);
530 (void)m_pBase->
read32(OhciInterruptStatus);
533 m_pBase->
write32(0, OhciControlHeadED);
534 m_pBase->
write32(0, OhciBulkHeadED);
535 (void)m_pBase->
read32(OhciHcca);
545 if (!Machine::instance().getIrqManager()->unregisterHandler(m_IrqId,
548 "OHCI teardown could not synchronously unregister its IRQ "
555 while (completions.
count()) {
556 auto* completion = completions.
popFront();
564 if (m_pPeriodicEDList && m_PeriodicEDBitmap.
test(0)) {
567 m_pPeriodicQueueTail =
nullptr;
573 assert(!m_pControlQueueHead && !m_pControlQueueTail);
574 assert(!m_pBulkQueueHead && !m_pBulkQueueTail);
575 assert(!m_pPeriodicQueueTail);
576 constexpr size_t FinalEdCount = OhciDescriptorRegionBytes /
sizeof(ED);
577 for (
size_t i = 0; i < FinalEdCount; ++i) {
578 assert(!m_ControlEDBitmap.
test(i));
579 assert(!m_BulkEDBitmap.
test(i));
580 assert(!m_PeriodicEDBitmap.
test(i));
582 constexpr size_t FinalTdCount = OhciDescriptorRegionBytes /
sizeof(TD);
583 for (
size_t i = 0; i < FinalTdCount; ++i)
584 assert(!m_TDBitmap.
test(i));
590 if (m_pBase && m_pHcca) {
592 m_pBase->
write32(resetControl, OhciControl);
593 (void)m_pBase->
read32(OhciControl);
599 m_pBase->
write32(OhciInterruptRhStsChange, enabled ? OhciInterruptEnable : OhciInterruptDisable);
600 (void)m_pBase->
read32(OhciInterruptEnable);
604 const uint32_t expected = control & OhciControlStateFunctionalMask;
605 m_pBase->
write32(control, OhciControl);
606 (void)m_pBase->
read32(OhciControl);
608 constexpr size_t TransitionPollLimit = 100;
609 size_t polls = TransitionPollLimit;
610 while (polls-- && ((m_pBase->
read32(OhciControl) & OhciControlStateFunctionalMask) != expected)) {
611 Time::delay(1 * Time::Multiplier::Millisecond);
614 if ((m_pBase->
read32(OhciControl) & OhciControlStateFunctionalMask) != expected)
615 panic(timeoutMessage);
621 if (!pED || !pED->pMetaData)
624#ifdef USB_VERBOSE_DEBUG
625 DEBUG_LOG(
"OHCI: removing ED #" << pED->pMetaData->id
626 <<
" from the schedule to prepare for reclamation");
629 const Lists type = pED->pMetaData->edType;
641 m_pBase->
write32(OhciInterruptStartOfFrame, OhciInterruptStatus);
642 m_pBase->
write32(OhciInterruptStartOfFrame, OhciInterruptEnable);
646 if (!pED || !pED->pMetaData)
650 pED->pMetaData->bIgnore =
true;
652 if (!pED->pMetaData->bLinked)
655 ED* pPrev = pED->pMetaData->pPrev;
656 ED* pNext = pED->pMetaData->pNext;
662 if (pED->pMetaData->edType == ControlList) {
663 pQueueHead = &m_pControlQueueHead;
664 pQueueTail = &m_pControlQueueTail;
666 }
else if (pED->pMetaData->edType == BulkList) {
667 pQueueHead = &m_pBulkQueueHead;
668 pQueueTail = &m_pBulkQueueTail;
671 ERROR(
"OHCI: ED #" << pED->pMetaData->id <<
" has an invalid type!");
676 bool bControl = pED->pMetaData->edType == ControlList;
679 if (pED == *pQueueHead) {
680#ifdef USB_VERBOSE_DEBUG
682 "OHCI: ED was a queue head, adjusting controller state "
693 pPrev->pNext = pED->pNext;
697 if (pED == *pQueueTail) {
703 pPrev->pMetaData->pNext = pNext;
705 pNext->pMetaData->pPrev = pPrev;
707 pED->pMetaData->pPrev =
nullptr;
708 pED->pMetaData->pNext =
nullptr;
709 pED->pMetaData->bLinked =
false;
736 if (!pED || !pED->pMetaData)
740 it != pED->pMetaData->completedTdList.
end(); ++it) {
741 const size_t tdId = (*it)->id;
742 ByteSet(*it, 0,
sizeof(
TD));
743 m_TDBitmap.
clear(tdId);
745 pED->pMetaData->completedTdList.
clear();
749 const size_t tdId = (*it)->id;
750 ByteSet(*it, 0,
sizeof(
TD));
751 m_TDBitmap.
clear(tdId);
753 pED->pMetaData->tdList.
clear();
754 pED->pMetaData->pFirstTD =
nullptr;
755 pED->pMetaData->pLastTD =
nullptr;
759 if (!pED || !pED->pMetaData)
764 const size_t id = metadata->id & OhciDescriptorOffsetMask;
765 const Lists type = metadata->edType;
766 const bool acceptedOperation = metadata->acceptedOperation;
768 ByteSet(pED, 0,
sizeof(
ED));
770 if (type == ControlList)
771 m_ControlEDBitmap.
clear(
id);
772 else if (type == BulkList)
773 m_BulkEDBitmap.
clear(
id);
774 else if (type == PeriodicList)
775 m_PeriodicEDBitmap.
clear(
id);
777 if (acceptedOperation)
783 if (!pED || !pED->pMetaData)
785 assert(pED && pED->pMetaData);
786 assert(!pED->pMetaData->bPeriodic);
787 assert(claim.generation == pED->pMetaData->completion.generation());
790 auto* cleanup =
new OhciCompletionCleanup{
this, pED, claim.generation};
792 claim.parameter, claim.result, finishDeferredCompletion,
798 if (!pED || !pED->pMetaData || pED->pMetaData->bPeriodic)
805 if (pED->pMetaData->completion.state() == UsbHcd::TransferCompletion::State::Idle) {
811 if (pED->pMetaData->completion.
claimForTeardown(-TransactionError, claim)) {
829 if (m_TeardownPhase == 2) {
835 return IrqDisposition::NotHandled;
838 uint32_t nStatus = m_pBase->
read32(OhciInterruptStatus) & m_pBase->
read32(OhciInterruptEnable);
839 const uint32_t observedDoneHead = m_pHcca->pDoneHead;
840 if (observedDoneHead) {
841 nStatus |= OhciInterruptWbDoneHead;
846 DEBUG_LOG(
"USB: OHCI: irq is not for us");
847 return IrqDisposition::NotHandled;
851 m_pBase->
write32(OhciInterruptMIE, OhciInterruptDisable);
852 (void)m_pBase->
read32(OhciInterruptEnable);
857 const uint32_t doneHead = m_pHcca->pDoneHead;
859 m_pHcca->pDoneHead = 0;
861 nStatus |= OhciInterruptWbDoneHead;
862 if (doneHead & 0x1) {
863 nStatus |= m_pBase->
read32(OhciInterruptStatus) & m_pBase->
read32(OhciInterruptEnable);
868 nStatus &= ~OhciInterruptMIE;
869 bool sofDrained =
true;
870 bool doneHeadDrained =
true;
872#ifdef USB_VERBOSE_DEBUG
873 DEBUG_LOG(
"OHCI: IRQ " << nStatus);
876 if (nStatus & OhciInterruptUnrecoverableError) {
880 ERROR(
"OHCI: controller is hung!");
881 return IrqDisposition::Handled;
884 if (nStatus & OhciInterruptStartOfFrame) {
885#ifdef USB_VERBOSE_DEBUG
886 DEBUG_LOG(
"OHCI: SOF, preparing to reclaim EDs...");
890 m_pBase->
write32(OhciInterruptStartOfFrame, OhciInterruptDisable);
893 constexpr size_t EdListCount = 3 * (OhciDescriptorRegionBytes /
sizeof(
ED));
894 size_t reclaimBudget = EdListCount;
895 while (reclaimBudget) {
907 const Lists type = pED->pMetaData->edType;
909 const bool ownsPublication = pED->pMetaData->completion.
claimCaptured(claim);
911#ifdef USB_VERBOSE_DEBUG
912 DEBUG_LOG(
"OHCI: freeing ED #" << pED->pMetaData->id <<
".");
928 ERROR_NOLOCK(
"OHCI: exceeded the SOF reclaim scan budget");
936 if (nStatus & OhciInterruptRhStsChange) {
942 m_pBase->
write32(OhciInterruptRhStsChange, OhciInterruptStatus);
943 (void)m_pBase->
read32(OhciInterruptStatus);
945 if (m_pBase->
read32(OhciRhStatus) & OhciRhHubStsOverCurrentCh) {
946 m_pBase->
write32(OhciRhHubStsOverCurrentCh, OhciRhStatus);
947 (void)m_pBase->
read32(OhciRhStatus);
950 for (
size_t i = 0; i < m_nPorts; i++) {
951 const size_t portRegister = OhciRhPortStatus + (i * 4);
952 const uint32_t portStatus = m_pBase->
read32(portRegister);
953 uint32_t acknowledgeMask = portStatus & (OhciRhPortStsEnableCh | OhciRhPortStsSuspendCh |
954 OhciRhPortStsOverCurrentCh);
960 if ((portStatus & OhciRhPortStsResCh) && !m_PortResetActive) {
961 acknowledgeMask |= OhciRhPortStsResCh;
964 if (portStatus & OhciRhPortStsConnStsCh) {
966 bool acknowledge = deferred;
967 size_t generation = 0;
969 const auto observation = m_PortChanges[i].observe();
970 acknowledge = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
973 generation = observation.generation;
974 m_DeferredPortChanges.defer(i, generation);
979 acknowledgeMask |= OhciRhPortStsConnStsCh;
986 m_RootHubStatusChangeDesired =
false;
991 if (acknowledgeMask) {
995 m_pBase->
write32(acknowledgeMask, portRegister);
996 (void)m_pBase->
read32(portRegister);
999 const size_t generation = m_DeferredPortChanges.release(i);
1001 m_PortChanges[i].acknowledge(generation);
1011 if (nStatus & OhciInterruptWbDoneHead) {
1012 constexpr size_t EdListCount = 3 * (OhciDescriptorRegionBytes /
sizeof(
ED));
1013 constexpr size_t TdListCount = OhciDescriptorRegionBytes /
sizeof(
TD);
1014 size_t scheduleBudget = EdListCount;
1016 while (scheduleBudget) {
1027 if (pED->pMetaData->bIgnore) {
1032 bool bPeriodic = pED->pMetaData->bPeriodic;
1036 size_t tdBudget = TdListCount;
1037 while (pED->pMetaData->tdList.
count() && tdBudget) {
1039 pTD = pED->pMetaData->tdList.
popFront();
1043 if (pTD->nStatus == 0xF) {
1050#ifdef USB_VERBOSE_DEBUG
1052 ERROR_NOLOCK(
"TD Error " <<
Dec << pTD->nStatus <<
Hex);
1054 nResult = -pTD->getError();
1056 if (pTD->pBufferStart) {
1058 size_t nBytesLeft = pTD->pBufferEnd - pTD->pBufferStart + 1;
1059 nResult = pTD->nBufferSize - nBytesLeft;
1061 nResult = pTD->nBufferSize;
1062 pED->pMetaData->nTotalBytes += nResult;
1064#ifdef USB_VERBOSE_DEBUG
1066 "TD #" <<
Dec << pTD->id <<
Hex <<
" [from ED #" <<
Dec << pED->pMetaData->id <<
Hex
1067 <<
"] DONE: " <<
Dec << pED->nAddress <<
":" << pED->nEndpoint <<
" "
1068 << (pTD->nPid == 1 ?
"OUT"
1069 : (pTD->nPid == 2 ?
"IN" : (pTD->nPid == 0 ?
"SETUP" :
"")))
1070 <<
" " << nResult <<
Hex);
1074 bool bEndOfTransfer =
1075 (!bPeriodic && ((nResult < 0) || (pTD == pED->pMetaData->pLastTD))) ||
1076 (bPeriodic && (nResult >= 0));
1079 pED->pMetaData->completedTdList.
pushBack(pTD);
1083 if (bEndOfTransfer) {
1084 const ssize_t completionResult = nResult < 0 ? nResult : pED->pMetaData->nTotalBytes;
1085 const bool ownsCompletion =
1086 bPeriodic || pED->pMetaData->completion.
captureNatural(completionResult);
1088 if (!bPeriodic && ownsCompletion) {
1091 }
else if (bPeriodic) {
1093 pTD->bDataToggle = !pTD->bDataToggle;
1097 pED->pMetaData->nTotalBytes = 0;
1100 if (bPeriodic && pED->pMetaData->
pCallback) {
1102 {pED->pMetaData->id, m_CompletionDeliveries.nextGeneration()},
1103 pED->pMetaData->
pCallback, pED->pMetaData->pParam, completionResult));
1110 pTD->pBufferStart = pTD->pBufferEnd - pTD->nBufferSize + 1;
1111 pED->pHeadTD = PHYS_TD(pTD->id) >> 4;
1113 pED->pMetaData->tdList.
pushBack(pTD);
1118 if (!tdBudget && pED->pMetaData->tdList.
count()) {
1119 doneHeadDrained =
false;
1120 ERROR_NOLOCK(
"OHCI: ED #" <<
Dec << pED->pMetaData->id <<
Hex
1121 <<
" exceeded the TD scan budget");
1126 if (!pED->pMetaData->bIgnore)
1133 doneHeadDrained =
false;
1134 ERROR_NOLOCK(
"OHCI: exceeded the done-head ED scan budget");
1141 if (persistList.
count()) {
1145 it = persistList.
erase(it);
1151 uint32_t acknowledgeStatus = nStatus & ~OhciInterruptRhStsChange;
1153 acknowledgeStatus &= ~OhciInterruptStartOfFrame;
1154 m_pBase->
write32(OhciInterruptStartOfFrame, OhciInterruptEnable);
1156 if (!doneHeadDrained) {
1157 acknowledgeStatus &= ~OhciInterruptWbDoneHead;
1159 if (acknowledgeStatus) {
1160 m_pBase->
write32(acknowledgeStatus, OhciInterruptStatus);
1161 (void)m_pBase->
read32(OhciInterruptStatus);
1164 if (m_TeardownPhase < 2) {
1165 m_pBase->
write32(OhciInterruptMIE, OhciInterruptEnable);
1168 if (completions.
count())
1172 while (completions.
count()) {
1173 auto* completion = completions.
popFront();
1177 return IrqDisposition::Handled;
1182 uintptr_t pBuffer,
size_t nBytes) {
1190 constexpr size_t EdCount = OhciDescriptorRegionBytes /
sizeof(
ED);
1191 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1192 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1194 bool valid = edOffset < EdCount;
1195 if (valid && transactionType == 0) {
1196 valid = m_ControlEDBitmap.
test(edOffset);
1197 pED = valid ? &m_pControlEDList[edOffset] :
nullptr;
1198 }
else if (valid && transactionType == 1) {
1199 valid = m_BulkEDBitmap.
test(edOffset);
1200 pED = valid ? &m_pBulkEDList[edOffset] :
nullptr;
1201 }
else if (valid && transactionType == 2) {
1202 valid = m_PeriodicEDBitmap.
test(edOffset);
1203 pED = valid ? &m_pPeriodicEDList[edOffset] :
nullptr;
1207 if (pTransaction ==
static_cast<uintptr_t
>(-1) || !valid || !pED || !pED->pMetaData ||
1208 pED->pMetaData->acceptedOperation || pED->pMetaData->bBuildFailed ||
1209 pED->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle) {
1210 ERROR(
"USB: OHCI: transaction " << pTransaction <<
" is invalid.");
1215 if (nIndex >= (OhciDescriptorRegionBytes /
sizeof(
TD))) {
1216 ERROR(
"USB: OHCI: TD space full");
1217 pED->pMetaData->bBuildFailed =
true;
1220 m_TDBitmap.
set(nIndex);
1223 TD* pTD = &m_pTDList[nIndex];
1224 ByteSet(pTD, 0,
sizeof(
TD));
1228 pTD->bBuffRounding = 1;
1251 const uintptr_t highestVirtual =
static_cast<uintptr_t
>(-1);
1252 if ((nBytes - 1) > highestVirtual - pBuffer) {
1253 ERROR(
"OHCI: addTransferToTransaction: buffer range wraps");
1254 pED->pMetaData->bBuildFailed =
true;
1255 ByteSet(pTD, 0,
sizeof(
TD));
1256 m_TDBitmap.
clear(nIndex);
1262 const uintptr_t lastBufferByte = pBuffer + nBytes - 1;
1263 const uintptr_t firstHardwarePage = pBuffer & ~uintptr_t(OhciHardwarePageBytes - 1);
1264 const uintptr_t lastHardwarePage = lastBufferByte & ~uintptr_t(OhciHardwarePageBytes - 1);
1265 const size_t hardwarePageCount =
1266 ((lastHardwarePage - firstHardwarePage) / OhciHardwarePageBytes) + 1;
1267 if (hardwarePageCount > OhciBufferPageCount) {
1268 ERROR(
"OHCI: addTransferToTransaction: buffer spans too many hardware pages");
1269 pED->pMetaData->bBuildFailed =
true;
1270 ByteSet(pTD, 0,
sizeof(
TD));
1271 m_TDBitmap.
clear(nIndex);
1276 physical_uintptr_t physicalStart = 0;
1277 physical_uintptr_t physicalEnd = 0;
1278 if (!DriverDma::virtualToPhysical(va, pBuffer, physicalStart) ||
1279 !DriverDma::virtualToPhysical(va, lastBufferByte, physicalEnd) || !physicalStart ||
1280 physicalStart > OhciHighestDmaAddress || physicalEnd > OhciHighestDmaAddress) {
1281 ERROR(
"OHCI: addTransferToTransaction: buffer range is not DMA-addressable");
1282 pED->pMetaData->bBuildFailed =
true;
1283 ByteSet(pTD, 0,
sizeof(
TD));
1284 m_TDBitmap.
clear(nIndex);
1290 if (!DriverDma::physicalEndpointsAreContiguous(physicalStart, physicalEnd, nBytes,
1291 OhciHighestDmaAddress)) {
1292 ERROR(
"OHCI: addTransferToTransaction: buffer is not physically contiguous");
1293 pED->pMetaData->bBuildFailed =
true;
1294 ByteSet(pTD, 0,
sizeof(
TD));
1295 m_TDBitmap.
clear(nIndex);
1299 pTD->pBufferStart = physicalStart;
1300 pTD->pBufferEnd = physicalEnd;
1301 pTD->nBufferSize = nBytes;
1308 if (pED->pMetaData->pLastTD) {
1309 pED->pMetaData->pLastTD->pNext = PHYS_TD(nIndex) >> 4;
1310 pED->pMetaData->pLastTD->nNextTDIndex = nIndex;
1311 pED->pMetaData->pLastTD->bLast =
false;
1313 pED->pMetaData->pFirstTD = pTD;
1314 pED->pHeadTD = PHYS_TD(nIndex) >> 4;
1316 pED->pMetaData->pLastTD = pTD;
1318 pED->pMetaData->tdList.
pushBack(pTD);
1324 return static_cast<uintptr_t
>(-1);
1327 bool bIsBulk = endpointInfo.nEndpoint > 0;
1335 if (nIndex >= (OhciDescriptorRegionBytes /
sizeof(
ED))) {
1336 ERROR(
"USB: OHCI: ED space full");
1337 return static_cast<uintptr_t
>(-1);
1341 m_BulkEDBitmap.
set(nIndex);
1342 pED = &m_pBulkEDList[nIndex];
1343 nIndex += OhciDescriptorRegionBytes;
1345 m_ControlEDBitmap.
set(nIndex);
1346 pED = &m_pControlEDList[nIndex];
1349 ByteSet(pED, 0,
sizeof(
ED));
1352 pED->nAddress = endpointInfo.nAddress;
1353 pED->nEndpoint = endpointInfo.nEndpoint;
1354 pED->bLoSpeed = endpointInfo.speed == LowSpeed;
1357 pED->nMaxPacketSize = endpointInfo.nMaxPacketSize;
1364 pED->pMetaData->endpointInfo = endpointInfo;
1365 pED->pMetaData->id = nIndex;
1366 pED->pMetaData->bIgnore =
true;
1367 pED->pMetaData->edType = bIsBulk ? BulkList : ControlList;
1368 pED->pMetaData->bPeriodic =
false;
1369 pED->pMetaData->bBuildFailed =
false;
1370 pED->pMetaData->pFirstTD = pED->pMetaData->pLastTD = 0;
1371 pED->pMetaData->nTotalBytes = 0;
1372 pED->pMetaData->pPrev = pED->pMetaData->pNext = 0;
1373 pED->pMetaData->bLinked =
false;
1375 pED->pMetaData->pParam = 0;
1376 pED->pMetaData->acceptedOperation =
false;
1382bool Ohci::doAsync(uintptr_t pTransaction,
void (*pCallback)(uintptr_t, ssize_t),
1393 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1394 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1395 constexpr size_t EdCount = OhciDescriptorRegionBytes /
sizeof(
ED);
1399 bool valid = edOffset < EdCount;
1400 if (valid && transactionType == 0) {
1401 valid = m_ControlEDBitmap.
test(edOffset);
1402 pED = valid ? &m_pControlEDList[edOffset] :
nullptr;
1404 }
else if (valid && transactionType == 1) {
1405 valid = m_BulkEDBitmap.
test(edOffset);
1406 pED = valid ? &m_pBulkEDList[edOffset] :
nullptr;
1411 if (pTransaction ==
static_cast<uintptr_t
>(-1) || !valid) {
1412 ERROR(
"OHCI: doAsync: didn't get a valid transaction id [" << pTransaction <<
", " << edOffset
1417 if (!pED->pMetaData) {
1418 ERROR(
"OHCI: doAsync: transaction metadata is missing");
1422 if (pED->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle) {
1423 ERROR(
"OHCI: doAsync: transaction is already accepted");
1427 if (pED->pMetaData->bBuildFailed || !pED->pMetaData->pLastTD) {
1428 ERROR(
"OHCI: doAsync: transaction could not be submitted [" << pTransaction <<
"].");
1437 pED->pMetaData->acceptedOperation =
true;
1439 const bool bControl = transactionType == 0;
1450 if (!m_pControlQueueHead) {
1451#ifdef USB_VERBOSE_DEBUG
1452 DEBUG_LOG(
"OHCI: ED is now the control queue head.");
1454 m_pControlQueueHead = pED;
1457 if (!m_pBulkQueueHead) {
1458#ifdef USB_VERBOSE_DEBUG
1459 DEBUG_LOG(
"OHCI: ED is now the control queue head.");
1461 m_pBulkQueueHead = pED;
1466 ED* pQueueHead =
nullptr;
1467 physical_uintptr_t queueHeadPhys = 0;
1469 pQueueHead = m_pControlQueueHead;
1470 queueHeadPhys =
vtp_ed(pQueueHead);
1472 pQueueHead = m_pBulkQueueHead;
1473 queueHeadPhys =
vtp_ed(pQueueHead);
1477 if (queueHeadPhys ==
vtp_ed(pED)) {
1479#ifdef USB_VERBOSE_DEBUG
1480 DEBUG_LOG(
"OHCI: new control queue head is " << queueHeadPhys <<
" compared to "
1481 << m_pBase->
read32(OhciControlHeadED));
1482 DEBUG_LOG(
"OHCI: current control queue ED is " << m_pBase->
read32(OhciControlCurrentED));
1484 m_pBase->
write32(queueHeadPhys, OhciControlHeadED);
1486#ifdef USB_VERBOSE_DEBUG
1487 DEBUG_LOG(
"OHCI: new bulk queue head is " << queueHeadPhys);
1489 m_pBase->
write32(queueHeadPhys, OhciBulkHeadED);
1494 ED* pTail =
nullptr;
1496 pTail = m_pControlQueueTail;
1497 m_pControlQueueTail = pED;
1499 pTail = m_pBulkQueueTail;
1500 m_pBulkQueueTail = pED;
1505 pTail->pNext =
vtp_ed(pED) >> 4;
1506 pTail->pMetaData->pNext = pED;
1510 pED->pMetaData->pNext =
nullptr;
1511 pED->pMetaData->pPrev = pTail;
1512 pQueueHead->pMetaData->pPrev =
nullptr;
1513 pED->pMetaData->bLinked =
true;
1526 pED->bSkip = pED->pMetaData->bIgnore =
false;
1529 start(pED->pMetaData->edType);
1534 uint32_t status = m_pBase->
read32(OhciCommandStatus);
1535 status |= bControl ? OhciCommandControlListFilled : OhciCommandBulkListFilled;
1536 m_pBase->
write32(status, OhciCommandStatus);
1546 bool drainDelivery =
false;
1556 uint32_t savedControl = m_pBase->
read32(OhciControl);
1557 m_pBase->
write32(OhciInterruptMIE, OhciInterruptDisable);
1559 (savedControl & ~OhciControlStateFunctionalMask) | OhciControlStateSuspended,
1560 "OHCI cancellation suspend timed out after 100 ms");
1562 bool restoreController =
false;
1567 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1568 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1569 constexpr size_t EdCount = OhciDescriptorRegionBytes /
sizeof(
ED);
1570 bool valid = edOffset < EdCount;
1571 if (valid && transactionType == 0) {
1572 valid = m_ControlEDBitmap.
test(edOffset);
1573 pED = valid ? &m_pControlEDList[edOffset] :
nullptr;
1574 }
else if (valid && transactionType == 1) {
1575 valid = m_BulkEDBitmap.
test(edOffset);
1576 pED = valid ? &m_pBulkEDList[edOffset] :
nullptr;
1580 Lists completedList = ControlList;
1581 if (valid && pED && pED->pMetaData) {
1584 pCallback, pParam, -TransactionError, claim);
1585 if (disposition == UsbHcd::TransferCompletion::CancellationDisposition::Claimed) {
1586 completedList = pED->pMetaData->edType;
1592 }
else if (disposition ==
1593 UsbHcd::TransferCompletion::CancellationDisposition::DrainPublished) {
1594 deliveryKey = {pTransaction, claim.generation};
1595 drainDelivery =
true;
1600 if (completions.
count())
1601 savedControl |=
static_cast<uint32_t
>(completedList);
1604 restoreController = m_TeardownPhase < 2;
1605 if (!restoreController) {
1606 m_pBase->
write32(OhciInterruptAll, OhciInterruptDisable);
1607 (void)m_pBase->
read32(OhciInterruptEnable);
1611 if (restoreController) {
1618 m_pBase->
write32(OhciInterruptMIE, OhciInterruptEnable);
1619 (void)m_pBase->
read32(OhciInterruptEnable);
1623 while (completions.
count()) {
1624 auto* completion = completions.
popFront();
1632 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
1633 bool producerAlreadyStopped) {
1637 (void)producerAlreadyStopped;
1638 panic(
"OHCI returned an interrupt-IN handle for an unsupported transfer");
1643 void (*pCallback)(uintptr_t, ssize_t),
1653 WARNING(
"USB: OHCI: recurring interrupt-IN transfers are not implemented");
1659 if (port >= m_nPorts) {
1660 ERROR(
"OHCI: invalid suppressed root-port replay " <<
Dec << port);
1666 if (m_TeardownPhase) {
1669 const auto observation = m_PortChanges[port].observe();
1670 const bool accepted = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
1672 m_PortChanges[port].acknowledge(observation.generation);
1676 m_TeardownPhase = 1;
1679 m_RootHubStatusChangeDesired =
false;
1682 ERROR(
"OHCI: live suppressed root-port replay could not be published");
1692 if (nPort >= m_nPorts) {
1699 const size_t portRegister = OhciRhPortStatus + (nPort * 4);
1707 m_PortResetActive =
true;
1712 m_pBase->
write32(OhciRhPortStsReset, portRegister);
1713 (void)m_pBase->
read32(portRegister);
1716 bool resetComplete =
false;
1717 constexpr size_t ResetPolls = 200;
1718 for (
size_t attempt = 0; attempt < ResetPolls; ++attempt) {
1719 if (m_pBase->
read32(portRegister) & OhciRhPortStsResCh) {
1720 resetComplete =
true;
1723 if (m_TeardownPhase) {
1726 Time::delay(5 * Time::Multiplier::Millisecond);
1734 const uint32_t portStatus = m_pBase->
read32(portRegister);
1735 resetComplete = resetComplete || (portStatus & OhciRhPortStsResCh);
1736 if (portStatus & OhciRhPortStsResCh) {
1737 m_pBase->
write32(OhciRhPortStsResCh, portRegister);
1738 (void)m_pBase->
read32(portRegister);
1742 if (resetComplete && !(m_pBase->
read32(portRegister) & OhciRhPortStsEnable)) {
1743 m_pBase->
write32(OhciRhPortStsEnable, portRegister);
1744 (void)m_pBase->
read32(portRegister);
1747 m_PortResetActive =
false;
1748 if (m_RootHubStatusChangeDesired) {
1755 if (!resetComplete && !m_TeardownPhase) {
1756 ERROR(
"OHCI: timed out resetting root port " << nPort);
1758 return resetComplete;
1762 uint64_t p6, uint64_t p7, uint64_t p8) {
1763 if (p1 >= m_nPorts) {
1772 if (m_pBase->
read32(OhciRhPortStatus + (p1 * 4)) & OhciRhPortStsConnected) {
1777 if (m_pBase->
read32(OhciRhPortStatus + (p1 * 4)) & OhciRhPortStsLoSpeed) {
1778 DEBUG_LOG(
"USB: OHCI: Port " <<
Dec << p1 <<
Hex
1779 <<
" has a low-speed device connected to it");
1782 DEBUG_LOG(
"USB: OHCI: Port " <<
Dec << p1 <<
Hex
1783 <<
" has a full-speed device connected to it");
1792 if (request.p1 < m_nPorts) {
1793 m_PortChanges[request.p1].cancel(
static_cast<size_t>(request.p8));
1801 uint32_t control = m_pBase->
read32(OhciControl);
1802 control &= ~static_cast<int>(list);
1803 m_pBase->
write32(control, OhciControl);
1810 uint32_t control = m_pBase->
read32(OhciControl);
1811 control |=
static_cast<int>(list);
1812 m_pBase->
write32(control, OhciControl);
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 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
void cancelRequest(const Request &request) override
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)
Adds a new transfer to an existent transaction.
IrqDisposition irq(irq_id_t number) override
IRQ handler.
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
void terminalizeEDForTeardown(ED *pED, List< UsbHcd::CallbackDeliveryQueue::Record * > &completions)
MUST_USE_RESULT bool cancelInterruptInAndDrain(const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter, bool producerAlreadyStopped) override
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
OperationBarrier m_CancellationOperations
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)
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
void transitionControllerState(uint32_t control, const char *timeoutMessage)
Spinlock m_ControlListChangeLock
Lock for changing the control list.
void removeFromFullSchedule(ED *pED)
void reclaimTransferDescriptors(ED *pED)
Spinlock m_PeriodicListChangeLock
Lock for changing the periodic list.
Spinlock m_BulkListChangeLock
Lock for changing the bulk list.
IrqProcessingLock m_IrqProcessingLock
Lock for modifying the schedule list itself (m_FullSchedule)
Lists
Enumeration of lists that can be stopped or started.
void stop(Lists list)
Stops the controller from processing the given list.
OperationBarrier m_AcceptedOperations
UsbHcd::CallbackDeliveryQueue::Record * prepareCompletion(ED *pED, const UsbHcd::TransferCompletion::Claim &claim)
void setRootHubStatusChangeSource(bool enabled)
List< ED * > m_FullSchedule
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 removeED(ED *pED)
Detaches an ED and queues it for reclamation at the next frame.
UsbHcd::CallbackDeliveryQueue m_CompletionDeliveries
OperationBarrier m_CallbackOperations
Spinlock m_RootHubLock
Serializes root-hub register access between reset and IRQ paths.
Spinlock m_DequeueListLock
Dequeue list lock.
OperationBarrier m_SubmissionOperations
void retireEDStorage(ED *pED)
void replaySuppressedConnectionChange(size_t port) override
void removeFromDequeueList(ED *pED)
List< ED * > m_DequeueList
List of EDs ready for dequeue (reclaiming)
ControllerLock m_PortResetMutex
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
ControllerLock m_Mutex
Global lock.
void start(Lists list)
Starts processing of the given list.
physical_uintptr_t vtp_ed(ED *pED)
Converts a software ED pointer to a physical address.
MUST_USE_RESULT bool tryAcquire(Lease &lease)
MUST_USE_RESULT bool tryEnter()
uint32_t readConfigSpace(Device *pDev, uint8_t offset)
void writeConfigSpace(Device *pDev, uint8_t offset, uint32_t data)
static const size_t continuous
static PhysicalMemoryManager & instance()
static const size_t below4GB
static ProcessorInformation & information()
bool acquire(bool recurse=false, bool safe=true)
static constexpr size_t getPageSize() noexcept
void deliver(Record *record)
bool drain(const Key &key)
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 claimForTeardown(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 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)