The Pedigree Project 0.1
Uhci.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#if X86_COMMON
21
22#include "Uhci.h"
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"
43
44#include "modules/drivers/common/DmaBuffer.h"
45#include "modules/system/usb/Usb.h"
46
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)))
51
52namespace {
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 {
61 Uhci* controller;
62 uintptr_t transaction;
63 size_t generation;
64};
65} // namespace
66
67static int threadStub(void* p) {
68 TerminationDeferral workerLifetime;
69 Uhci* pUhci = reinterpret_cast<Uhci*>(p);
70 pUhci->doDequeue();
71 return 0;
72}
73
74Uhci::Uhci(Device* pDev)
75 : UsbHub(pDev),
76 RequestQueue(MakeConstantString("UHCI")),
77 m_pBase(0),
78 m_SubmissionOperations(),
79 m_CancelOperations(),
80 m_TransferOperations(),
81 m_CallbackOperations(),
82 m_IrqId(0),
83 m_TimerRegistered(false),
84 m_InterruptsClosing(false),
85 m_nPorts(0),
86 m_PortChangesClosing(false),
87 m_PortChangeLock(),
88 m_Mutex(),
89 m_IrqProcessingLock(),
90 m_CompletionDeliveries(),
91 m_AsyncQueueListChangeLock(),
92 m_pFrameList(nullptr),
93 m_pFrameListPhys(0),
94 m_pTDList(nullptr),
95 m_pTDListPhys(0),
96 m_pAsyncQH(nullptr),
97 m_pPeriodicQH(nullptr),
98 m_pQHList(nullptr),
99 m_pQHListPhys(0),
100 m_UhciMR("Uhci-MR"),
101 m_pCurrentAsyncQueueTail(0),
102 m_pCurrentAsyncQueueHead(0),
103 m_AsyncSchedule(),
104 m_PeriodicSchedule(),
105 m_DequeueList(),
106 m_DequeueCount(0),
107 m_DequeueOperations(),
108 m_pDequeueThread(nullptr),
109 m_nPortCheckTicks(0) {
110 setSpecificType(String("UHCI"));
111
112 // Verify that IRQs are enabled - we need them!
115
116 auto& pci = PciBus::instance();
117 PciFunctionState::State pciState{};
118 if (!pci.inspectFunction(this, pciState)) {
119 ERROR("UHCI: unsupported inherited PCI function state");
120 return;
121 }
122 const uint32_t bar = pciState.bars[4];
123 const uint32_t base = bar & ~3U;
124 Device::Address* mapping = nullptr;
125 for (auto* address : addresses())
126 if (address->m_Name == "bar4" && (bar & 1U) && base && address->m_IsIoSpace &&
127 address->m_Address == base)
128 mapping = address;
129 if (!mapping || mapping->m_Size < 0x20 || !pci.updateCommand(this, 0, 1))
130 return;
131 mapping->map();
132 m_pBase = mapping->m_Io;
133 if (!m_pBase)
134 return;
135
136 if (TargetInfo::getPageSize() < UhciHardwareFrameListBytes ||
137 (TargetInfo::getPageSize() % UhciHardwareFrameListBytes)) {
138 ERROR("USB: UHCI: target pages cannot align the 4 KiB hardware frame list");
139 return;
140 }
141
142 // Allocate the memory region
143 if (!PhysicalMemoryManager::instance().allocateRegion(
144 m_UhciMR, DriverDma::pageCountForBytes(UhciDmaRegionBytes),
147 ERROR("USB: UHCI: Couldn't allocate memory region!");
148 return;
149 }
150
151 uintptr_t pVirtBase = reinterpret_cast<uintptr_t>(m_UhciMR.virtualAddress());
152 physical_uintptr_t pPhysBase = m_UhciMR.physicalAddress();
153
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);
157
158 m_pFrameListPhys = pPhysBase;
159 m_pQHListPhys = pPhysBase + UhciHardwareFrameListBytes;
160 m_pTDListPhys = m_pQHListPhys + UhciQhRegionBytes;
161
162 // Allocate room for the Dummy QH
163 m_QHBitmap.set(0);
164 QH* pDummyQH = &m_pQHList[0];
165
166 // Set all frame list entries to the dummy QH
167 DoubleWordSet(m_pFrameList, m_pQHListPhys | 2, 0x400);
168
169 ByteSet(pDummyQH, 0, sizeof(QH));
170
171 pDummyQH->pNext = m_pQHListPhys >> 4;
172 pDummyQH->bNextQH = 1;
173 pDummyQH->bElemInvalid = 1;
174
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;
191
192 m_pCurrentAsyncQueueTail = m_pCurrentAsyncQueueHead = pDummyQH;
193
194 // Dequeue main thread
195 m_pDequeueThread = new Thread(Processor::information().getCurrentThread()->getParent(),
196 threadStub, reinterpret_cast<void*>(this));
197
198 // Disable legacy emulation and SMI generation before taking the controller.
199 if (!setLegacySupportControl(0x8f00)) {
200 ERROR("UHCI: legacy SMI sources did not disable");
201 return;
202 }
203 m_HardwareOwned = true;
204 m_pBase->write16(0, UHCI_INTR);
205 (void)m_pBase->read16(UHCI_INTR);
206
207 // Stop a running controller (BIOS may have started it up). Unset the
208 // configured flag, as we are no longer configured properly.
209 m_pBase->write16(m_pBase->read16(UHCI_CMD) & ~0x40, UHCI_CMD);
210 stop();
211 m_pBase->write16(m_pBase->read16(UHCI_STS) & 0x1f, UHCI_STS);
212
213 // Reset the host controller
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");
221
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");
225 return;
226 }
227 FENCE();
228 if (!pci.updateCommand(this, 0, 5 | 0x400))
229 return;
230
231 // Write frame list pointer
232 m_pBase->write32(m_pFrameListPhys, UHCI_FRLP);
233
234 // Close and flush the controller source before registering with the PIC.
235 // Registration unmasks the shared line immediately.
236 m_pBase->write16(0, UHCI_INTR);
237 (void)m_pBase->read16(UHCI_INTR);
238 const uint16_t pendingStatus = m_pBase->read16(UHCI_STS);
239 if (pendingStatus) {
240 m_pBase->write16(pendingStatus, UHCI_STS);
241 (void)m_pBase->read16(UHCI_STS);
242 }
243
244 m_IrqId = Machine::instance().getIrqManager()->registerPciIrqHandler(
245 static_cast<IrqHandler*>(this), this, IrqPolicy::pciIntxThreaded());
246 if (!m_IrqId) {
247 panic("UHCI could not register its PCI IRQ handler");
248 }
249 Machine::instance().getIrqManager()->control(
250 getInterruptNumber(), IrqManager::MitigationThreshold,
251 (1500000 / 64)); // 12KB/ms (12Mbps) in bytes, divided by 64 bytes
252 // maximum per transfer/IRQ
253
255#if THREADS
256 if (getLifecycleState() != RequestQueue::LifecycleState::Accepting) {
257 panic("UHCI request queue did not enter the accepting state");
258 }
259#endif
260
261 // Start the controller: 64-byte reclamation and CF set, as well as run bit.
262 // Also, force a global resume of all ports out of any form of suspend state
263 m_pBase->write16(0xC1 | 0x10, UHCI_CMD);
264 Time::delay(10 * Time::Multiplier::Millisecond);
265 m_pBase->write16(0xC1, UHCI_CMD);
266 start();
267
268 EMIT_IF(UsbVerboseDebug) {
269 DEBUG_LOG("USB: UHCI: Reset complete");
270 }
271
272 // Give time for ports to resume and stabilise.
273 Time::delay(100 * Time::Multiplier::Millisecond);
274
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)) {
279 break; // Controllers must have 2 ports, but can have up to 7 by
280 // the spec.
281 }
282
283 ++m_nPorts;
284 }
285
286 if (!UsbHcd::validUhciRootPortCount(m_nPorts)) {
287 panic("UHCI detected an unsupported root-port count");
288 }
289
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");
293 }
294 }
295
296 // Every fallible publication setup is complete. The controller ran with
297 // its source masked up to this point, so registration failures cannot leak
298 // an unserviceable level interrupt onto the shared PCI line.
299 if (!setLegacySupportControl(0x2000) || !pci.updateCommand(this, 0x400, 5)) {
300 ERROR("UHCI: could not enable its PCI interrupt route");
301 return;
302 }
303 m_pBase->write16(0xf, UHCI_INTR);
304 (void)m_pBase->read16(UHCI_INTR);
305
306 for (size_t i = 0; i < m_nPorts; ++i) {
307 if (!portReset(i)) {
308 continue;
309 }
310
311 const size_t portRegister = UHCI_PORTSC + (i * 2);
312 {
313 LockGuard<Spinlock> portChangeGuard(m_PortChangeLock);
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)),
319 portRegister);
320 (void)m_pBase->read16(portRegister);
321 }
322 }
323#if THREADS
324 // Device discovery constructs the controller while holding the global
325 // device-tree lock. Let the queue worker enumerate after that factory
326 // callback returns instead of recursively acquiring the same lock here.
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");
330 }
331 m_PortChanges[i].acknowledge(observation.generation);
332#else
333 executeRequest(i);
334#endif
335 }
336
337 // Install the timer handler for the periodic port checks. A threadless
338 // build cannot safely enumerate a device from timer interrupt context.
339#if THREADS
340 Timer* timer = Machine::instance().getTimer();
341 if (timer) {
342 m_TimerRegistered = timer->registerHandler(this);
343 }
344 if (!m_TimerRegistered) {
345 ERROR("UHCI could not register its root-port polling callback");
346 return;
347 }
348 if (timer->supportsDeadlines() &&
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;
353 return;
354 }
355#endif
356 m_Initialised = true;
357}
358
360 QH* pQH = reinterpret_cast<QH*>(context);
361 assert(pQH && pQH->pMetaData && pQH->pMetaData->pOwner);
362 Uhci* pOwner = pQH->pMetaData->pOwner;
363 {
364 LockGuard<Mutex> queueGuard(pOwner->m_AsyncQueueListChangeLock);
365 pOwner->m_DequeueList.pushBack(pQH);
366 }
367 pOwner->m_DequeueCount.release();
368}
369
371 assert(pQH && pQH->pMetaData);
372
373 for (List<QH*>::Iterator it = m_AsyncSchedule.begin(); it != m_AsyncSchedule.end();) {
374 if (*it == pQH) {
375 m_AsyncSchedule.erase(it);
376 break;
377 }
378 ++it;
379 }
380
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) {
385 if (*it == pQH) {
386 m_PeriodicSchedule.erase(it);
387 break;
388 }
389 }
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;
396 pPrev->bNextQH = 1;
397 pPrev->bNextInvalid = 0;
398 if (pQH == m_pCurrentAsyncQueueTail)
400 }
401
402 pQH->pMetaData->pPrev = nullptr;
403 pQH->pMetaData->pNext = nullptr;
404 pQH->pMetaData->bIgnore = true;
405}
406
408 DoubleWordSet(m_pFrameList, m_pQHListPhys | 2U, 1024);
409 // Every branch shares phase zero. Inserting faster intervals first makes
410 // each slower queue's successor identical in every frame containing it.
411 for (size_t interval = 1; interval <= 128; interval *= 2) {
412 for (auto it = m_PeriodicSchedule.begin(); it != m_PeriodicSchedule.end(); ++it) {
413 QH* qh = *it;
414 if (qh->pMetaData->periodicInterval != interval)
415 continue;
416 qh->pNext = m_pFrameList[0] >> 4;
417 qh->bNextQH = 1;
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;
422 }
423 }
424 FENCE();
425}
426
427void Uhci::rearmPeriodicCompletion(void* context) {
428 auto* rearm = static_cast<PeriodicCompletion*>(context);
429 Uhci* controller = rearm->controller;
430 LockGuard<Mutex> transactionGuard(controller->m_Mutex);
431 LockGuard<Mutex> irqGuard(controller->m_IrqProcessingLock);
432 if (controller->m_InterruptsClosing || !controller->m_QHBitmap.test(rearm->transaction))
433 return;
434 QH* qh = &controller->m_pQHList[rearm->transaction];
435 auto* metadata = qh->pMetaData;
436 if (!metadata || !metadata->pPeriodicCallback ||
437 metadata->periodicGeneration != rearm->generation)
438 return;
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) ||
444 endpoint.nRootPortGeneration != controller->currentRootPortGeneration(port))
445 return;
446 if (!(controller->m_pBase->read16(UHCI_CMD) & UHCI_CMD_RUN) ||
447 (controller->m_pBase->read16(UHCI_STS) & UHCI_STS_HALT))
448 return;
449 // The host caches QH element pointers. Halt before replacing that pointer
450 // so a late writeback cannot undo the rearm after the client releases DMA.
451 controller->stop();
452 TD* td = metadata->pFirstTD;
453 td->nStatus = 0;
454 td->nActLen = 0x7ff;
455 td->nErr = 3;
456 td->bIoc = 1;
457 td->bDataToggle = !td->bDataToggle;
458 metadata->periodicPending = false;
459 metadata->nTotalBytes = 0;
460 FENCE();
461 td->nStatus = 0x80;
462 qh->pElem = (controller->m_pTDListPhys + td->id * sizeof(TD)) >> 4;
463 qh->bElemQH = 0;
464 qh->bElemInvalid = 0;
465 FENCE();
466 controller->start();
467}
468
469void Uhci::destroyPeriodicCompletion(void* context) {
470 delete static_cast<PeriodicCompletion*>(context);
471}
472
474 assert(pQH && pQH->pMetaData);
475
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);
481 }
482
483 while (pQH->pMetaData->tdList.count()) {
484 TD* pTD = pQH->pMetaData->tdList.popFront();
485 const size_t id = pTD->id;
486 ByteSet(pTD, 0, sizeof(TD));
487 m_TDBitmap.clear(id);
488 }
489
490 const size_t id = pQH->pMetaData->id;
491 delete pQH->pMetaData;
492 ByteSet(pQH, 0, sizeof(QH));
493 m_QHBitmap.clear(id);
494}
495
496Uhci::~Uhci() {
498 List<QH*> periodicRetirements;
499
500 // The timer is the only port-change producer, and unregistration drains
501 // any callback already admitted by the timer registry.
502 Timer* timer = Machine::instance().getTimer();
503 if (m_TimerRegistered) {
504 if (!timer || !timer->unregisterHandler(this)) {
505 panic(
506 "UHCI teardown could not synchronously unregister its timer "
507 "callback");
508 }
509 m_TimerRegistered = false;
510 }
511
512 {
513 LockGuard<Spinlock> portChangeGuard(m_PortChangeLock);
514 m_PortChangesClosing = true;
515 }
516 for (size_t i = 0; i < m_nPorts; ++i) {
517 m_PortChanges[i].stopAfterQuiesce();
518 }
520
521 // Enumeration is quiesced, while transfer cancellation and DMA are still
522 // live for class-driver destructors.
524
525 // Port enumeration has drained. No new transaction may now cross the DMA
526 // publication boundary, but accepted transfers retain ownership until the
527 // dequeue worker has reclaimed their descriptors. Submission callers stay
528 // admitted until after this boundary so an in-flight caller either linked
529 // before close or observes the closed transfer admission and rolls back.
531
532 {
533 LockGuard<Mutex> transactionGuard(m_Mutex);
535 m_InterruptsClosing = true;
536 if (m_HardwareOwned) {
537 m_pBase->write16(0, UHCI_INTR);
538 (void)m_pBase->read16(UHCI_INTR);
539 }
540 if (m_HardwareOwned && !setLegacySupportControl(0x8f00))
541 panic("UHCI teardown could not disable legacy interrupts");
542 // Close admission while the IRQ serialization lock keeps a level
543 // interrupt from repeatedly entering the just-closed callback path.
544 m_CallbackOperations.close();
545
546 if (m_HardwareOwned) {
547 // Fatal controller errors bypass USBINTR. USBPIRQDEN above keeps
548 // them off the shared line while the callback admission is
549 // closed and the synchronous DMA halt completes.
550 stop();
551 const uint16_t pending = m_pBase->read16(UHCI_STS) & 0x1f;
552 if (pending) {
553 m_pBase->write16(pending, UHCI_STS);
554 (void)m_pBase->read16(UHCI_STS);
555 }
556 }
557
558 if (m_pQHList) {
559 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
560 while (m_AsyncSchedule.count()) {
561 QH* pQH = m_AsyncSchedule.popFront();
562 assert(pQH && pQH->pMetaData);
563
564 if (pQH->pMetaData->bPeriodic) {
565 const bool dequeueAdmitted = m_DequeueOperations.tryEnter();
566 assert(dequeueAdmitted);
567 if (dequeueAdmitted)
568 periodicRetirements.pushBack(pQH);
569 } else {
571 const bool claimed =
572 pQH->pMetaData->completion.claimForTeardown(-TransactionError, claim);
573 assert(claimed);
574 if (claimed) {
575 const bool dequeueAdmitted = m_DequeueOperations.tryEnter();
576 assert(dequeueAdmitted);
577 if (dequeueAdmitted) {
578 teardownCompletions.pushBack(m_CompletionDeliveries.create(
579 {pQH->pMetaData->id, claim.generation}, claim.callback, claim.parameter,
580 claim.result, enqueueCompletedTransfer, pQH));
581 }
582 }
583 }
584
586 }
587 }
588
589 if (teardownCompletions.count())
590 m_CompletionDeliveries.publish(teardownCompletions);
591
592 if (m_HardwareOwned) {
593 m_pBase->write32(0, UHCI_FRLP);
594 (void)m_pBase->read32(UHCI_FRLP);
595 }
596 }
597
600
601 // These callbacks can release synchronous USB waits owned by a callback
602 // which entered before callback admission closed.
603 while (teardownCompletions.count()) {
604 auto* completion = teardownCompletions.popFront();
606 }
607
608 if (m_HardwareOwned && !PciBus::instance().updateCommand(this, 4, 0x400))
609 panic("UHCI teardown could not disable PCI DMA and INTx");
610
611 if (m_IrqId) {
612 if (!Machine::instance().getIrqManager()->unregisterHandler(m_IrqId,
613 static_cast<IrqHandler*>(this))) {
614 panic(
615 "UHCI teardown could not synchronously unregister its IRQ "
616 "callback");
617 }
618 m_IrqId = 0;
619 }
620 m_CallbackOperations.wait();
621
622 // Teardown and already-admitted IRQ callbacks were allowed to
623 // synchronously cancel or drain peer records from the fully-published
624 // batch above.
627
628 // No producer remains. This is normally already empty, but draining makes
629 // the teardown invariant explicit if a peer callback stole a batch record.
631
632 // Existing periodic samples are covered by m_CallbackOperations. Retire
633 // their QHs only after those samples have returned; the periodic API has
634 // no terminal-callback contract and its current consumers ignore errors.
635 size_t periodicCount = 0;
636 {
637 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
638 while (periodicRetirements.count()) {
639 m_DequeueList.pushBack(periodicRetirements.popFront());
640 ++periodicCount;
641 }
642 }
643 while (periodicCount) {
644 --periodicCount;
646 }
647
652 if (m_pDequeueThread) {
653 if (!m_pDequeueThread->joinForCompletion())
654 panic("UHCI teardown could not join its dequeue worker");
655 m_pDequeueThread = nullptr;
656 }
657
659 assert(m_SubmissionOperations.isClosedAndDrained());
660 assert(m_CancelOperations.isClosedAndDrained());
661 assert(m_CallbackOperations.isClosedAndDrained());
662 assert(m_DequeueOperations.isClosedAndDrained());
663 assert(m_TransferOperations.isClosedAndDrained());
664 {
665 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
666 assert(!m_AsyncSchedule.count());
667 assert(!m_DequeueList.count());
668 }
669
670 if (m_pQHList) {
671 LockGuard<Mutex> transactionGuard(m_Mutex);
672 constexpr size_t QhListCount = UhciQhRegionBytes / sizeof(QH);
673 constexpr size_t TdListCount = UhciTdRegionBytes / sizeof(TD);
674
675 // Transactions built but never accepted were never DMA-owned and do
676 // not carry a retained transfer admission.
677 for (size_t i = 1; i < QhListCount; ++i) {
678 if (!m_QHBitmap.test(i))
679 continue;
680 QH* pQH = &m_pQHList[i];
681 if (!pQH->pMetaData)
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");
686 }
687
688 for (size_t i = 1; i < QhListCount; ++i)
689 assert(!m_QHBitmap.test(i));
690 for (size_t i = 0; i < TdListCount; ++i)
691 assert(!m_TDBitmap.test(i));
692
693 QH* pDummyQH = &m_pQHList[0];
694 assert(m_QHBitmap.test(0));
695 delete pDummyQH->pMetaData;
696 ByteSet(pDummyQH, 0, sizeof(QH));
697 m_QHBitmap.clear(0);
698 assert(!m_QHBitmap.test(0));
699 m_pCurrentAsyncQueueHead = nullptr;
700 m_pCurrentAsyncQueueTail = nullptr;
701 }
702}
703
704void Uhci::doDequeue() {
705 while (true) {
706 if (m_DequeueOperations.isClosedAndDrained()) {
707 return;
708 }
709
710 const bool dequeuing = m_DequeueCount.acquireForCompletion();
711 (void)dequeuing;
712
713 QH* pQH = 0;
714 {
715 LockGuard<Mutex> guard(m_AsyncQueueListChangeLock);
716 pQH = m_DequeueList.popFront();
717 }
718
719 if (!pQH)
720 continue;
721
722 {
723 LockGuard<Mutex> transactionGuard(m_Mutex);
725 }
728
729 EMIT_IF(UsbVerboseDebug) {
730 DEBUG_LOG("Dequeue complete.");
731 }
732 }
733}
734
735IrqDisposition Uhci::irq(irq_id_t number) {
736 (void)number;
737
739 if (!m_CallbackOperations.tryAcquire(callback)) {
741 }
743 {
744 LockGuard<Mutex> transactionGuard(m_IrqProcessingLock);
745
746 if (m_InterruptsClosing) {
748 }
749
750 uint16_t nStatus = m_pBase->read16(UHCI_STS);
751
752 if (!nStatus) {
753 return IrqDisposition::NotHandled; // Shared IRQ: another device
754 }
755
756 // Retire only the captured causes before scanning. A completion which
757 // arrives after its QH has been visited then relatches status and
758 // causes a fresh threaded pass instead of being erased by a trailing
759 // W1C.
760 m_pBase->write16(nStatus, UHCI_STS);
761 (void)m_pBase->read16(UHCI_STS);
762
763 EMIT_IF(UsbVerboseDebug) {
764 DEBUG_LOG("UHCI IRQ " << nStatus);
765 }
766
767 List<QH*> persistList;
768
769 // Because there's no IOC for *every* transfer, we need to handle errors
770 // that occur before the last transfer. These will create an error
771 // status only.
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;
776 QH* pQH = 0;
777 while (qhBudget) {
778 --qhBudget;
779 {
780 LockGuard<Mutex> guard(m_AsyncQueueListChangeLock);
781 if (m_AsyncSchedule.count())
782 pQH = m_AsyncSchedule.popFront();
783 else
784 break;
785 }
786
787 {
788 bool bPeriodic = pQH->pMetaData->bPeriodic;
789 if (bPeriodic && pQH->pMetaData->periodicPending) {
790 persistList.pushBack(pQH);
791 continue;
792 }
793
794 // Iterate the TD list
795 TD* pTD = 0;
796 size_t tdBudget = TdListCount;
797 while (pQH->pMetaData->tdList.count() && tdBudget) {
798 --tdBudget;
799 pTD = pQH->pMetaData->tdList.popFront();
800
801 // If we've already detected that this TD was a short
802 // transfer, don't process any more TDs
803 if (pTD->bShortTransferTD)
804 break;
805
806 bool bEndOfTransfer = false;
807 if (pTD->nStatus & 0x80) {
808 pQH->pMetaData->tdList.pushFront(pTD);
809 break;
810 }
811 FENCE();
812
813 if (bPeriodic) {
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) ||
820 endpoint.nRootPortGeneration != currentRootPortGeneration(rootPort))) {
821 // A physical disconnect can complete its periodic TD before
822 // the port worker reaches the class-driver destructor. Leave
823 // the inactive descriptor owned by its handle, but neither
824 // report nor rearm a sample from the absent device.
825 pQH->pMetaData->bIgnore = true;
826 pQH->pMetaData->tdList.pushBack(pTD);
827 break;
828 }
829 }
830
831 ssize_t nResult;
832 if (pTD->nStatus & 0x7e) {
833 // #ifdef USB_VERBOSE_DEBUG
834 ERROR_NOLOCK("UHCI TD ERROR!");
835 ERROR_NOLOCK("TD Status: " << pTD->nStatus << " [" << pTD->nErr
836 << "], USB status: " << nStatus);
837 // #endif
838 nResult = -pTD->getError();
839 } else {
840 nResult = (pTD->nActLen + 1) % 0x800;
841 pQH->pMetaData->nTotalBytes += nResult;
842 }
843 EMIT_IF(UsbVerboseDebug) {
844 DEBUG_LOG_NOLOCK("TD #" << Dec << pTD->id << " [QH #" << pQH->pMetaData->id << Hex
845 << "] DONE: " << Dec << pTD->nAddress << ":" << pTD->nEndpoint
846 << " "
847 << (pTD->nPid == UsbPidOut
848 ? "OUT"
849 : (pTD->nPid == UsbPidIn
850 ? "IN"
851 : (pTD->nPid == UsbPidSetup ? "SETUP" : "")))
852 << " " << nResult << Hex);
853 }
854
855 // Handle the "end of transfer" cases
856 bEndOfTransfer = bPeriodic || (nResult < 0) || (pTD == pQH->pMetaData->pLastTD);
857
858 // Some extra cases we need to handle
859 if ((pTD != pQH->pMetaData->pLastTD) && !bEndOfTransfer) {
860 // Control endpoints are irrelevant here
861 if (pTD->nEndpoint) {
862 if ((nResult >= 0) && (pTD->nPid == UsbPidIn)) {
863 // Check for a short read. There is no point
864 // continuing to read from the device if
865 // it's sent us less than we asked for
866 // before the last TD. This stops the
867 // transfer hanging on IN transactions that
868 // return less data than expected.
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;
874 }
875 }
876 }
877 }
878
879 if (!bPeriodic)
880 pQH->pMetaData->completedTdList.pushBack(pTD);
881
882 // Last TD or error condition, if async, otherwise only
883 // when it gives no error
884 if (bEndOfTransfer) {
885 const ssize_t completionResult = nResult < 0 ? nResult : pQH->pMetaData->nTotalBytes;
886
887 if (!bPeriodic) {
888 const bool captured = pQH->pMetaData->completion.captureNatural(completionResult);
889 assert(captured);
890 if (captured) {
891 // The completion lock excludes cancellation
892 // while this establishes the DMA boundary.
893 stop();
894
896 const bool claimed = pQH->pMetaData->completion.claimCaptured(claim);
897 const bool dequeueAdmitted = m_DequeueOperations.tryEnter();
898 assert(claimed && dequeueAdmitted);
899 if (claimed && dequeueAdmitted) {
900 {
901 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
903 }
904
905 completions.pushBack(m_CompletionDeliveries.create(
906 {pQH->pMetaData->id, claim.generation}, claim.callback, claim.parameter,
907 claim.result, enqueueCompletedTransfer, pQH));
908 }
909
910 start();
911 }
912 } else {
913 pQH->pMetaData->periodicPending = true;
914 // UHCI also raises IOC when revisiting an inactive TD.
915 pTD->bIoc = 0;
916 FENCE();
917
918 if (pQH->pMetaData->pPeriodicCallback) {
919 PeriodicCompletion* rearm =
920 nResult >= 0 ? new PeriodicCompletion{this, pQH->pMetaData->id,
921 pQH->pMetaData->periodicGeneration}
922 : nullptr;
923 completions.pushBack(m_CompletionDeliveries.create(
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));
929 }
930 }
931 }
932
933 // The callback owns the inactive report until it returns. Errors
934 // remain inactive until cancellation or endpoint recovery.
935 if (bPeriodic) {
936 pQH->pMetaData->tdList.pushBack(pTD);
937 break; // Periodic QHs should only have one TD
938 }
939 }
940
941 if (!tdBudget && pQH->pMetaData->tdList.count()) {
942 ERROR_NOLOCK("UHCI: QH #" << Dec << pQH->pMetaData->id << Hex
943 << " exceeded the TD scan budget");
944 }
945 }
946
947 if (!pQH->pMetaData->bIgnore)
948 persistList.pushBack(pQH);
949 }
950
951 {
952 LockGuard<Mutex> guard(m_AsyncQueueListChangeLock);
953 if (m_AsyncSchedule.count()) {
954 ERROR_NOLOCK("UHCI: exceeded the QH scan budget");
955 }
956 }
957 }
958
959 if (persistList.count()) {
960 LockGuard<Mutex> guard(m_AsyncQueueListChangeLock);
961 for (List<QH*>::Iterator it = persistList.begin(); it != persistList.end();) {
962 m_AsyncSchedule.pushBack(*it);
963 it = persistList.erase(it);
964 }
965 }
966
967 if (completions.count())
968 m_CompletionDeliveries.publish(completions);
969 }
970
971 while (completions.count()) {
972 auto* completion = completions.popFront();
974 }
975
976 return IrqDisposition::Handled;
977}
978
979void Uhci::addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid,
980 uintptr_t pBuffer, size_t nBytes) {
981 OperationBarrier::Lease submission;
982 if (!m_SubmissionOperations.tryAcquire(submission))
983 return;
984
985 LockGuard<Mutex> transactionGuard(m_Mutex);
986 size_t nIndex = 0;
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");
991 return;
992 }
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");
998 return;
999 }
1000 if (nBytes > UhciMaximumTransferBytes) {
1001 ERROR("USB: UHCI: transfer exceeds one TD");
1002 pQH->pMetaData->bBuildFailed = true;
1003 return;
1004 }
1005 nIndex = m_TDBitmap.getFirstClear();
1006 if (nIndex >= (UhciTdRegionBytes / sizeof(TD))) {
1007 ERROR("USB: UHCI: TD space full");
1008 pQH->pMetaData->bBuildFailed = true;
1009 return;
1010 }
1011 m_TDBitmap.set(nIndex);
1012
1013 // Grab the TD
1014 TD* pTD = &m_pTDList[nIndex];
1015 ByteSet(pTD, 0, sizeof(TD));
1016 pTD->bNextInvalid = 1; // Assume next is invalid, will be zeroed if another TD is linked
1017 pTD->id = nIndex;
1018
1019 // Active, and only allow one retry
1020 pTD->nStatus = 0x80;
1021 pTD->bIoc = 0; // Don't issue an interrupt on completion until the very
1022 // last TD in the transaction.
1023 pTD->nErr = 3;
1024
1025 // PID for this transfer
1026 pTD->nPid = pid;
1027
1028 // Speed information
1029 pTD->bLoSpeed = pQH->pMetaData->endpointInfo.speed == LowSpeed;
1030
1031 // Stop before remaining bulk IN packets can consume the next BOT phase.
1032 pTD->bSpd = pid == UsbPidIn && pQH->pMetaData->endpointInfo.nEndpoint;
1033
1034 // Endpoint information
1035 pTD->nAddress = pQH->pMetaData->endpointInfo.nAddress;
1036 pTD->nEndpoint = pQH->pMetaData->endpointInfo.nEndpoint;
1037 pTD->bDataToggle = bToggle;
1038
1039 // Transfer information
1040 pTD->nMaxLen = nBytes ? nBytes - 1 : 0x7ff;
1041 pTD->nBufferSize = nBytes;
1042 if (nBytes) {
1043 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
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);
1051 return;
1052 }
1053 pTD->pBuff = physicalBuffer;
1054 }
1055
1056 // Link into the existing TD list
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;
1061 // pQH->pMetaData->pLastTD->bNextDepth = 1;
1062 } else {
1063 pQH->pMetaData->pFirstTD = pTD;
1064 pQH->pElem = PHYS_TD(nIndex) >> 4;
1065 pQH->bElemInvalid = 0;
1066 pQH->bElemQH = 0;
1067 }
1068 pQH->pMetaData->pLastTD = pTD;
1069
1070 pQH->pMetaData->tdList.pushBack(pTD);
1071}
1072
1073uintptr_t Uhci::createTransaction(UsbEndpoint endpointInfo) {
1074 OperationBarrier::Lease submission;
1075 if (!m_SubmissionOperations.tryAcquire(submission))
1076 return static_cast<uintptr_t>(-1);
1077
1078 LockGuard<Mutex> transactionGuard(m_Mutex);
1079 size_t nIndex = 0;
1080 if (!m_pQHList)
1081 return static_cast<uintptr_t>(-1);
1082 nIndex = m_QHBitmap.getFirstClear();
1083 if (nIndex >= (UhciQhRegionBytes / sizeof(QH))) {
1084 ERROR("USB: UHCI: QH space full");
1085 return static_cast<uintptr_t>(-1);
1086 }
1087 m_QHBitmap.set(nIndex);
1088
1089 // Grab the QH
1090 QH* pQH = &m_pQHList[nIndex];
1091 ByteSet(pQH, 0, sizeof(QH));
1092
1093 // Only need to configure metadata, everything else is set during linkage
1094 // and TD creation
1095 pQH->pMetaData = new QH::MetaData;
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;
1111
1112 return nIndex;
1113}
1114
1115bool Uhci::doAsync(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
1116 uintptr_t pParam) {
1117 return doAsyncOwned(pTransaction, pCallback, pParam, nullptr, 0);
1118}
1119
1120bool Uhci::doAsyncOwned(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
1121 uintptr_t pParam, UsbInterruptInHandle* interruptHandle,
1122 size_t interruptGeneration) {
1123 OperationBarrier::Lease submission;
1124 if (!m_SubmissionOperations.tryAcquire(submission))
1125 return false;
1126
1127 LockGuard<Mutex> transactionGuard(m_Mutex);
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 << "].");
1132 return false;
1133 }
1134
1135 QH* pQH = &m_pQHList[pTransaction];
1136 if (!pQH->pMetaData) {
1137 ByteSet(pQH, 0, sizeof(QH));
1138 m_QHBitmap.clear(pTransaction);
1139 return false;
1140 }
1141 if (pQH->pMetaData->bBuildFailed || !pQH->pMetaData->pLastTD) {
1142 ERROR("UHCI: doAsync: transaction could not be submitted [" << pTransaction << "].");
1144 return false;
1145 }
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 << "].");
1150 return false;
1151 }
1152
1155 return false;
1156 }
1157
1159 if (m_InterruptsClosing || !m_pCurrentAsyncQueueTail) {
1161 ERROR("UHCI: Queue tail is null!");
1164 return false;
1165 }
1166 if (pQH->pMetaData->bPeriodic) {
1167 size_t busTime = pQH->pMetaData->periodicBusTime;
1168 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
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");
1175 return false;
1176 }
1177 }
1178
1179 // m_IrqProcessingLock is the controller-wide DMA publication lock.
1180 stop();
1181 {
1182 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
1183
1184 pQH->pMetaData->bIgnore = true;
1185 if (pQH->pMetaData->bPeriodic) {
1186 pQH->pMetaData->pPrev = pQH->pMetaData->pNext = pQH;
1187 m_PeriodicSchedule.pushBack(pQH);
1189 } else {
1190 const size_t queueHeadIndex = (reinterpret_cast<uintptr_t>(m_pCurrentAsyncQueueHead) -
1191 reinterpret_cast<uintptr_t>(m_pQHList)) /
1192 sizeof(QH);
1193 pQH->pNext = (m_pQHListPhys + (queueHeadIndex * sizeof(QH))) >> 4;
1194 pQH->bNextInvalid = 0;
1195 pQH->bNextQH = 1;
1196 QH* pOldTail = m_pCurrentAsyncQueueTail;
1198 pOldTail->pNext = (m_pQHListPhys + (pTransaction * sizeof(QH))) >> 4;
1199 pOldTail->bNextInvalid = 0;
1200 pOldTail->bNextQH = 1;
1201 pOldTail->pMetaData->pNext = pQH;
1202 pQH->pMetaData->pNext = m_pCurrentAsyncQueueHead;
1203 pQH->pMetaData->pPrev = pOldTail;
1204 m_pCurrentAsyncQueueHead->pMetaData->pPrev = pQH;
1205 }
1206 m_AsyncSchedule.pushBack(pQH);
1207
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 ||
1213 !publishInterruptInHandle(*interruptHandle, {pTransaction, interruptGeneration},
1214 pCallback, pParam)) {
1215 panic("UHCI could not publish interrupt-IN ownership before DMA");
1216 }
1217 } else {
1218 pQH->pMetaData->completion.arm(pCallback, pParam, m_CompletionDeliveries.nextGeneration());
1219 }
1220
1221 // Active is published only after both linked-list representations and
1222 // the callback obligation are complete.
1223 pQH->pMetaData->bIgnore = false;
1224 }
1225
1226 start();
1227 return true;
1228}
1229
1230void Uhci::cancelAsyncAndDrain(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
1231 uintptr_t pParam) {
1232 OperationBarrier::Lease cancellation;
1233 if (!m_CancelOperations.tryAcquire(cancellation))
1234 return;
1235
1237 bool drainDelivery = false;
1238 UsbHcd::CallbackDeliveryQueue::Key deliveryKey = {0, 0};
1239
1240 {
1241 LockGuard<Mutex> transactionGuard(m_Mutex);
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) {
1249 const auto disposition = pQH->pMetaData->completion.claimCancellation(
1250 pCallback, pParam, -TransactionError, claim);
1251
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);
1256 stop();
1257
1258 const bool dequeueAdmitted = m_DequeueOperations.tryEnter();
1259 assert(dequeueAdmitted);
1260 if (dequeueAdmitted) {
1261 {
1262 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
1264 }
1265
1266 completions.pushBack(m_CompletionDeliveries.create(
1267 {pQH->pMetaData->id, claim.generation}, claim.callback, claim.parameter,
1268 claim.result, enqueueCompletedTransfer, pQH));
1269 m_CompletionDeliveries.publish(completions);
1270 }
1271
1272 if (!m_InterruptsClosing) {
1273 start();
1274 m_pBase->write16(interruptMask, UHCI_INTR);
1275 } else {
1276 m_pBase->write16(0, UHCI_INTR);
1277 }
1278 (void)m_pBase->read16(UHCI_INTR);
1279 } else if (disposition ==
1280 UsbHcd::TransferCompletion::CancellationDisposition::DrainPublished) {
1281 deliveryKey = {pTransaction, claim.generation};
1282 drainDelivery = true;
1283 }
1284 }
1285 }
1286 }
1287
1288 while (completions.count()) {
1289 auto* completion = completions.popFront();
1290 m_CompletionDeliveries.deliver(completion);
1291 }
1292
1293 if (drainDelivery)
1294 (void)m_CompletionDeliveries.drain(deliveryKey);
1295}
1296
1298 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
1299 bool producerAlreadyStopped) {
1300 OperationBarrier::Lease cancellation;
1301 if (!m_CancelOperations.tryAcquire(cancellation))
1302 panic("UHCI interrupt-IN cancellation raced controller teardown");
1303
1304 if (!producerAlreadyStopped) {
1305 bool matched = false;
1306 {
1307 LockGuard<Mutex> transactionGuard(m_Mutex);
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];
1311 QH::MetaData* metadata = pQH->pMetaData;
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);
1318 stop();
1319
1320 {
1321 LockGuard<Mutex> queueGuard(m_AsyncQueueListChangeLock);
1323 }
1324 metadata->pPeriodicCallback = nullptr;
1325 metadata->pPeriodicParam = 0;
1328 matched = true;
1329
1330 if (!m_InterruptsClosing) {
1331 start();
1332 m_pBase->write16(interruptMask, UHCI_INTR);
1333 }
1334 (void)m_pBase->read16(UHCI_INTR);
1335 }
1336 }
1337 }
1338
1339 if (!matched)
1340 panic("UHCI interrupt-IN handle lost its controller subscription");
1341 }
1342
1343 return m_CompletionDeliveries.cancelSubscription(token.transaction, token.generation);
1344}
1345
1346bool Uhci::addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes,
1347 void (*pCallback)(uintptr_t, ssize_t),
1348 UsbInterruptInHandle& handle, uintptr_t pParam) {
1349 OperationBarrier::Lease submission;
1350 if (!m_SubmissionOperations.tryAcquire(submission) || handle || !pCallback)
1351 return false;
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))
1356 return false;
1357
1358 // Create a new transaction
1359 uintptr_t nTransaction = createTransaction(endpointInfo);
1360 if (nTransaction == static_cast<uintptr_t>(-1)) {
1361 ERROR("USB: UHCI: Couldn't create interrupt transaction!");
1362 return false;
1363 }
1364
1365 // Get the QH and set the periodic flag
1366 QH* pQH = &m_pQHList[nTransaction];
1367 const size_t generation = m_CompletionDeliveries.nextGeneration();
1368 {
1369 LockGuard<Mutex> transactionGuard(m_Mutex);
1370 pQH->pMetaData->bPeriodic = true;
1371 pQH->pMetaData->periodicGeneration = generation;
1372 size_t interval = 1;
1373 while (interval * 2 <= endpointInfo.nInterval)
1374 interval *= 2;
1375 pQH->pMetaData->periodicInterval = interval;
1376 // Reserve a conservative worst-case packet time, including low-speed
1377 // preambles and bit stuffing. All branches share a phase, so sum directly.
1378 pQH->pMetaData->periodicBusTime =
1379 (nBytes + 32U) * (endpointInfo.speed == LowSpeed ? 7U : 1U) + 20U;
1380 }
1381
1382 // Add a single transfer to the transaction
1383 addTransferToTransaction(nTransaction, false, UsbPidIn, pBuffer, nBytes);
1384 if (!pQH->pMetaData->pLastTD) {
1385 ERROR("USB: UHCI: Couldn't add transfer to transaction!");
1386 LockGuard<Mutex> transactionGuard(m_Mutex);
1387 if (m_QHBitmap.test(nTransaction) && pQH->pMetaData)
1389 return false;
1390 }
1391
1392 // Let doAsync do the rest
1393 if (!doAsyncOwned(nTransaction, pCallback, pParam, &handle, generation)) {
1394 ERROR("USB: UHCI: Couldn't submit interrupt transaction!");
1395 LockGuard<Mutex> transactionGuard(m_Mutex);
1396 if (m_QHBitmap.test(nTransaction) && pQH->pMetaData)
1398 return false;
1399 }
1400
1401 return true;
1402}
1403
1404void Uhci::modifyPortControl(size_t portRegister, uint16_t clearMask, uint16_t setMask) {
1405 constexpr uint16_t ChangeMask = UHCI_PORTSC_CSCH | UHCI_PORTSC_EDCH;
1406 LockGuard<Spinlock> portChangeGuard(m_PortChangeLock);
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);
1412}
1413
1414bool Uhci::portReset(uint8_t nPort, bool bErrorResponse) {
1415 EMIT_IF(UsbVerboseDebug) {
1416 DEBUG_LOG("USB: UHCI: Reset on port " << nPort);
1417 }
1418
1419 const size_t portRegister = UHCI_PORTSC + (nPort * 2);
1420 constexpr uint16_t ChangeMask = UHCI_PORTSC_CSCH | UHCI_PORTSC_EDCH;
1421
1422 if (bErrorResponse) {
1423 // Before port reset, disable the port
1424 modifyPortControl(portRegister, UHCI_PORTSC_ENABLE, 0);
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);
1429 }
1430 if (m_pBase->read16(portRegister) & UHCI_PORTSC_ENABLE) {
1431 ERROR("USB: UHCI: Port " << Dec << nPort << Hex << " did not disable within 100 ms");
1432 return false;
1433 }
1434 }
1435
1436 // Perform a reset of the port
1437 modifyPortControl(portRegister, 0, UHCI_PORTSC_PRES);
1438 Time::delay(50 * Time::Multiplier::Millisecond);
1439 modifyPortControl(portRegister, UHCI_PORTSC_PRES, 0);
1440
1441 // Enable the port
1442 modifyPortControl(portRegister, 0, UHCI_PORTSC_ENABLE);
1443 Time::delay((bErrorResponse ? 500 : 100) * Time::Multiplier::Millisecond);
1444
1445 // Check that the device is completely enabled
1446 if (!(m_pBase->read16(portRegister) & UHCI_PORTSC_ENABLE)) {
1447 // #ifdef USB_VERBOSE_DEBUG
1448 DEBUG_LOG("USB: UHCI: During reset, port "
1449 << nPort << " could not be enabled. It may become enabled soon.");
1450 // #endif
1451 return false;
1452 }
1453
1454 // Retire the enable-change generated by reset. Leave CSC asserted for the
1455 // timer so a disconnect/reconnect during reset cannot be erased here.
1456 {
1457 LockGuard<Spinlock> portChangeGuard(m_PortChangeLock);
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)),
1462 portRegister);
1463 (void)m_pBase->read16(portRegister);
1464 }
1465 }
1466
1467 // Verify that we have a device connected here
1468 if (!(m_pBase->read16(portRegister) & UHCI_PORTSC_CONN)) {
1469 // #ifdef USB_VERBOSE_DEBUG
1470 DEBUG_LOG("USB: UHCI: During reset, port "
1471 << nPort
1472 << " was enabled but had no device on it. A device may be detected "
1473 "shortly.");
1474 // #endif
1475 return false;
1476 }
1477
1478 EMIT_IF(UsbVerboseDebug) {
1479 DEBUG_LOG("USB: Post-reset status is " << m_pBase->read16(portRegister));
1480 }
1481
1482 return true;
1483}
1484
1485uint64_t Uhci::executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5,
1486 uint64_t p6, uint64_t p7, uint64_t p8) {
1487 if (p1 >= m_nPorts) {
1488 return 0;
1489 }
1490 UsbHcd::PortChangeRequest::Completion completion(m_PortChanges[p1], static_cast<size_t>(p8));
1491 if (!completion) {
1492 return 0;
1493 }
1494
1495 const uint16_t portStatus = m_pBase->read16(UHCI_PORTSC + (p1 * 2));
1496
1497 // Check for a connected device
1498 if (portStatus & UHCI_PORTSC_CONN) {
1499 // Determine the speed of the attached device
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");
1503 if (!deviceConnected(p1, LowSpeed))
1504 WARNING("USB: UHCI [" << this << "]: Port " << Dec << p1 << Hex
1505 << " appeared to be connected but could not be set up");
1506 } else {
1507 DEBUG_LOG("USB: UHCI [" << this << "]: Port " << Dec << p1 << Hex
1508 << " has a full-speed device connected to it");
1509 if (!deviceConnected(p1, FullSpeed))
1510 WARNING("USB UHCI [" << this << "]: Port " << Dec << p1 << Hex
1511 << " appeared to be connected but could not be set up");
1512 }
1513 } else {
1514 DEBUG_LOG("USB: UHCI [" << this << "]: Device on port " << Dec << p1 << Hex
1515 << " disconnected.");
1517 }
1518 return 0;
1519}
1520
1521void Uhci::cancelRequest(const Request& request) {
1522 if (request.p1 < m_nPorts) {
1523 m_PortChanges[request.p1].cancel(request.p8);
1524 }
1525}
1526
1527void Uhci::timer(uint64_t delta) {
1528#if !THREADS
1529 (void)delta;
1530 return;
1531#else
1532 OperationBarrier::Lease callback;
1533 if (!m_CallbackOperations.tryAcquire(callback)) {
1534 return;
1535 }
1536
1537 uint64_t nextPoll = Time::Multiplier::Millisecond;
1538 {
1539 LockGuard<Spinlock> portChangeGuard(m_PortChangeLock);
1540 if (m_PortChangesClosing) {
1541 return;
1542 }
1543
1544 m_nPortCheckTicks += delta;
1545 if (m_nPortCheckTicks < 1000000) {
1546 nextPoll -= m_nPortCheckTicks;
1547 } else {
1548 // We check the ports once in a Millisecond.
1550
1551 // Check every port for a change
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;
1558
1559 if (portStatus & UHCI_PORTSC_CSCH) {
1561 acknowledgeMask |= UHCI_PORTSC_CSCH;
1562 } else {
1563 const auto observation = m_PortChanges[i].observe();
1564 if (UsbHcd::PortChangeRequest::canAcknowledge(observation.result)) {
1565 acknowledgeMask |= UHCI_PORTSC_CSCH;
1566 acknowledgeGeneration = observation.generation;
1567 }
1568 }
1569 }
1570
1571 if (acknowledgeMask) {
1572 m_pBase->write16(UsbHcd::selectiveW1cValue(portStatus, ChangeMask, acknowledgeMask),
1573 portRegister);
1574 (void)m_pBase->read16(portRegister);
1575 }
1576 if (acknowledgeGeneration) {
1577 m_PortChanges[i].acknowledge(acknowledgeGeneration);
1578 }
1579 }
1580 }
1581 }
1582 Timer* source = Machine::instance().getTimer();
1583 if (source && source->supportsDeadlines() &&
1584 !source->armHandler(this, Time::getTicks() + nextPoll)) {
1585 panic("UHCI could not rearm its root-port polling callback");
1586 }
1587#endif
1588}
1589
1591#if THREADS
1592 if (port >= m_nPorts) {
1593 ERROR("UHCI: invalid suppressed root-port replay " << Dec << port);
1594 return;
1595 }
1596
1597 LockGuard<Spinlock> portChangeGuard(m_PortChangeLock);
1598 // Teardown stops publication before its active enumeration worker returns.
1599 if (m_PortChangesClosing) {
1600 return;
1601 }
1602 const auto observation = m_PortChanges[port].observe();
1603 const bool accepted = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
1604 if (accepted) {
1605 m_PortChanges[port].acknowledge(observation.generation);
1606 return;
1607 }
1608
1609 m_PortChangesClosing = true;
1610 ERROR("UHCI: live suppressed root-port replay could not be published");
1611 assert(false);
1612#else
1613 (void)port;
1614#endif
1615}
1616
1617size_t Uhci::currentRootPortGeneration(size_t port) const {
1618 return port < m_nPorts ? m_PortChanges[port].observedGeneration() : 0;
1619}
1620
1622 m_pBase->write16(m_pBase->read16(UHCI_CMD) & ~1, UHCI_CMD);
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");
1629}
1630
1632 m_pBase->write16(m_pBase->read16(UHCI_CMD) | 1, UHCI_CMD);
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");
1639}
1640
1641bool Uhci::setLegacySupportControl(uint16_t control) {
1642 // LEGSUP has separate RO shadows and W1C status; readback verifies only
1643 // the writable interrupt/emulation enables, including the PIRQ gate.
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);
1649}
1650
1651#endif
IoBase * m_Io
Definition Device.h:115
size_t m_Size
Definition Device.h:109
void map(size_t forcedSize=0, bool bUser=false, bool bWriteCombine=false, bool bWriteThrough=false)
Definition Device.cc:346
bool test(size_t n) const
void clear(size_t n)
void set(size_t n)
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)
Definition IrqManager.cc:55
Definition List.h:61
Iterator begin()
Definition List.h:122
::Iterator< T, node_t > Iterator
Definition List.h:67
Iterator end()
Definition List.h:132
virtual Timer * getTimer()=0
MUST_USE_RESULT bool tryAcquire(Lease &lease)
MUST_USE_RESULT bool tryEnter()
static PhysicalMemoryManager & instance()
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
virtual void destroy()
virtual void initialise()
void release(size_t n=1)
Definition Semaphore.cc:549
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:372
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
bool joinForCompletion()
Definition Thread.cc:2713
virtual bool supportsDeadlines() const
virtual bool armHandler(TimerHandler *, uint64_t)
Definition Uhci.h:54
static void enqueueCompletedTransfer(void *context)
Definition Uhci.cc:359
Mutex m_IrqProcessingLock
Definition Uhci.h:277
IrqDisposition irq(irq_id_t number) override
IRQ handler.
Definition Uhci.cc:735
size_t currentRootPortGeneration(size_t port) const override
Definition Uhci.cc:1617
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)
Definition Uhci.cc:1485
QH * m_pCurrentAsyncQueueHead
Definition Uhci.h:304
void replaySuppressedConnectionChange(size_t port) override
Definition Uhci.cc:1590
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
Definition Uhci.cc:1230
bool setLegacySupportControl(uint16_t control)
Definition Uhci.cc:1641
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
Definition Uhci.cc:1115
List< QH * > m_AsyncSchedule
List of QHs in the active asynchronous schedule.
Definition Uhci.h:307
Semaphore m_DequeueCount
Semaphore for the dequeue list.
Definition Uhci.h:314
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
Definition Uhci.cc:1414
OperationBarrier m_SubmissionOperations
Definition Uhci.h:259
void cancelRequest(const Request &request) override
Definition Uhci.cc:1521
void rebuildPeriodicScheduleLocked()
Definition Uhci.cc:407
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.
Definition Uhci.cc:1346
void detachQueueHeadLocked(QH *pQH)
Definition Uhci.cc:370
void stop()
Stops the controller or panics before DMA ownership can be transferred.
Definition Uhci.cc:1621
void modifyPortControl(size_t portRegister, uint16_t clearMask, uint16_t setMask)
Serialises control RMWs with scanning without echoing W1C status bits.
Definition Uhci.cc:1404
MUST_USE_RESULT bool cancelInterruptInAndDrain(const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter, bool producerAlreadyStopped) override
Definition Uhci.cc:1297
List< QH * > m_DequeueList
List of QHs ready for dequeue.
Definition Uhci.h:311
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
Definition Uhci.cc:1073
uint64_t m_nPortCheckTicks
The time passed since last port check.
Definition Uhci.h:324
void timer(uint64_t delta)
Timer callback to handle port status changes.
Definition Uhci.cc:1527
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)
Adds a new transfer to an existent transaction.
Definition Uhci.cc:979
UsbHcd::CallbackDeliveryQueue m_CompletionDeliveries
Definition Uhci.h:279
QH * m_pCurrentAsyncQueueTail
Definition Uhci.h:300
OperationBarrier m_DequeueOperations
Definition Uhci.h:320
OperationBarrier m_TransferOperations
Definition Uhci.h:263
void reclaimQueueHeadLocked(QH *pQH)
Definition Uhci.cc:473
OperationBarrier m_CancelOperations
Definition Uhci.h:261
void start()
Starts the controller or panics instead of leaving work silently stuck.
Definition Uhci.cc:1631
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.
Definition UsbHub.cc:387
void deviceDisconnected(uint8_t nPort)
Called when a device is disconnected from a port on the hub.
Definition UsbHub.cc:545
MUST_USE_RESULT bool publishInterruptInHandle(UsbInterruptInHandle &handle, const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter)
Definition UsbHub.cc:206
MUST_USE_RESULT bool deferConnectionChangeIfSuppressed(size_t port)
Definition UsbHub.cc:302
void disconnectAllDevices()
Definition UsbHub.cc:216
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117
#define assert(x)
Definition assert.h:39
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
IrqDisposition
Definition IrqHandler.h:31
Iterator erase(Iterator &Iter)
Definition List.h:352
T popFront()
Definition List.h:330
void pushFront(const T &value)
Definition List.h:300
size_t count() const
Definition List.h:212
void pushBack(const T &value)
Definition List.h:216
UsbHcd::TransferCompletion completion
Definition Uhci.h:148