The Pedigree Project 0.1
Ohci.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#include "Ohci.h"
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"
46
47#include "modules/drivers/common/DmaBuffer.h"
48#include "modules/system/usb/Usb.h"
49#include "modules/system/usb/UsbHub.h"
50
51namespace {
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;
63} // namespace
64
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)))
68
69namespace {
70struct OhciCompletionCleanup {
71 Ohci* controller;
72 Ohci::ED* ed;
73 size_t generation;
74};
75} // namespace
76
77void Ohci::finishDeferredCompletion(void* context) {
78 auto* cleanup = reinterpret_cast<OhciCompletionCleanup*>(context);
79 Ohci* controller = cleanup->controller;
80 {
81 LockGuard<ControllerLock> guard(controller->m_Mutex);
82 ED* ed = cleanup->ed;
83 if (ed->pMetaData && ed->pMetaData->completion.generation() == cleanup->generation) {
84 controller->retireEDStorage(ed);
85 } else
86 panic("OHCI completion cleanup lost its transaction generation");
87 }
88 delete cleanup;
89}
90
91Ohci::Ohci(Device* pDev)
92 : UsbHub(pDev),
93 RequestQueue(MakeConstantString("OHCI")),
94 m_pBase(0),
95 m_nPorts(0),
96 m_Initialised(false),
97 m_Mutex(),
98 m_PortResetMutex(),
99 m_pHcca(nullptr),
100 m_pHccaPhys(0),
101 m_IrqProcessingLock(),
102 m_CompletionDeliveries(),
103 m_RootHubLock(),
104 m_RootHubStatusChangeDesired(false),
105 m_PortResetActive(false),
106 m_TeardownPhase(0),
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),
116 m_ControlEDBitmap(),
117 m_pBulkEDList(nullptr),
118 m_pBulkEDListPhys(0),
119 m_BulkEDBitmap(),
120 m_pTDList(nullptr),
121 m_pTDListPhys(0),
122 m_TDBitmap(),
123 m_pBulkQueueHead(nullptr),
124 m_pControlQueueHead(nullptr),
125 m_pBulkQueueTail(nullptr),
126 m_pControlQueueTail(nullptr),
127 m_pPeriodicQueueTail(nullptr),
128 m_DequeueListLock(),
129 m_DequeueList(),
130 m_DequeueCount(0),
131 m_OhciMR("Ohci-MR"),
132 m_CallbackOperations(),
133 m_SubmissionOperations(),
134 m_CancellationOperations(),
135 m_AcceptedOperations(),
136 m_IrqId(0) {
137 setSpecificType(String("OHCI"));
138
139#if !X86_COMMON
140 // Completion and root-port processing require ordinary thread context.
141 // No maintained non-x86 machine provides a supported OHCI IRQ path.
142 ERROR("OHCI requires threaded PCI IRQ delivery");
143 return;
144#endif
145
146 if (TargetInfo::getPageSize() < OhciHardwarePageBytes ||
147 (TargetInfo::getPageSize() % OhciHardwarePageBytes)) {
148 ERROR("OHCI: target pages cannot represent 4 KiB OHCI buffer pages");
149 return;
150 }
151
152 // Allocate the memory region
153 if (!PhysicalMemoryManager::instance().allocateRegion(
154 m_OhciMR, DriverDma::pageCountForBytes(OhciDmaRegionBytes),
157 ERROR("USB: OHCI: Couldn't allocate memory region!");
158 return;
159 }
160
161 uintptr_t virtualBase = reinterpret_cast<uintptr_t>(m_OhciMR.virtualAddress());
162 uintptr_t physicalBase = m_OhciMR.physicalAddress();
163
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);
169
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;
175
176 // Clear out the HCCA block.
177 ByteSet(m_pHcca, 0, 0x800);
178
179 // Get an ED for the periodic list
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;
198
199 // Set all HCCA interrupt ED entries to our periodic ED
200 DoubleWordSet(m_pHcca->pInterruptEDList, m_pPeriodicEDListPhys, 3);
201
202 // Every periodic ED will be added after this one
203 m_pPeriodicQueueTail = pPeriodicED;
204
205 m_pBulkQueueTail = m_pBulkQueueHead = 0;
206 m_pControlQueueTail = m_pControlQueueHead = 0;
207
208#if X86_COMMON
209 // Make sure bus mastering and MMIO are enabled.
210 uint32_t nPciCmdSts = PciBus::instance().readConfigSpace(this, 1);
211 PciBus::instance().writeConfigSpace(this, 1, nPciCmdSts | 0x6);
212#endif
213
214 // Grab the ports
215 m_pBase = m_Addresses[0]->m_Io;
216 m_Addresses[0]->map();
217
218 // Dump the version of the controller and a nice little banner.
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 << ".");
222
223 // Do not let a firmware-programmed source reach the PCI line while the
224 // controller is being taken over and reset.
225 m_pBase->write32(OhciInterruptAll, OhciInterruptDisable);
226 (void)m_pBase->read32(OhciInterruptEnable);
227
228 // Determine first of all if the HC is controlled by the BIOS.
229 uint32_t control = m_pBase->read32(OhciControl);
230 if (control & OhciControlInterruptRoute) {
231 // SMM.
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);
241 }
242 } else {
243 // Chances are good that the BIOS has the thing running.
244 if (control & OhciControlStateFunctionalMask) {
245 DEBUG_LOG("USB: OHCI: BIOS is currently in charge.");
246 } else {
247 DEBUG_LOG("USB: OHCI: not yet operational.");
248 }
249
250 // Throw the controller into operational mode if it isn't.
251 if (!(control & OhciControlStateRunning))
252 m_pBase->write32(OhciControlStateRunning, OhciControl);
253 }
254
255 // Perform a reset via the UHCI Control register.
256 m_pBase->write32(control & ~OhciControlStateFunctionalMask, OhciControl);
257 Time::delay(200 * Time::Multiplier::Millisecond);
258
259 // Grab the FM Interval register (5.1.1.4, OHCI spec).
260 uint32_t interval = m_pBase->read32(OhciFmInterval);
261
262 // Perform a full hardware reset.
263 m_pBase->write32(OhciCommandHcReset, OhciCommandStatus);
264 constexpr size_t ResetPollLimit = 20;
265 size_t resetPolls = ResetPollLimit;
266 while (m_pBase->read32(OhciCommandStatus) & OhciCommandHcReset) {
267 if (!resetPolls--)
268 panic("OHCI controller reset timed out after 100 ms");
269 Time::delay(5 * Time::Multiplier::Millisecond);
270 }
271
272 // We now have 2 ms to complete all operations before we start the
273 // controller. 5.1.1.4, OHCI spec.
274
275 // Set up the HCCA block.
276 m_pBase->write32(m_pHccaPhys, OhciHcca);
277
278 // Set up the operational registers.
279 m_pBase->write32(m_pControlEDListPhys, OhciControlHeadED);
280 m_pBase->write32(m_pBulkEDListPhys, OhciBulkHeadED);
281
282 // Reset may restore interrupt state, so keep the device silent until its
283 // IRQ callback and preallocated port publications are ready.
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);
288
289 // Prepare the control register
290 control = m_pBase->read32(OhciControl);
291 control &= ~(0x3 | 0x3C | OhciControlStateFunctionalMask |
292 OhciControlInterruptRoute); // Control bulk service, List enable, etc
293 control |= OhciControlListsEnable | OhciControlStateRunning | 0x3; // 4:1 control/bulk ED ratio
294 m_pBase->write32(control, OhciControl);
295
296 // Controller is now running. Yay!
297
298 // Restore the Frame Interval register (reset by a HC reset)
299 m_pBase->write32(interval | (1U << 31U), OhciFmInterval);
300
301 DEBUG_LOG("USB: OHCI: maximum packet size is " << ((interval >> 16) & 0xEFFF));
302
303 // Turn on all ports on the root hub.
304 m_pBase->write32(OhciRhHubStsSetGlobalPower, OhciRhStatus);
305
306 // Set up the RequestQueue
307 initialise();
308
309#if THREADS
310 if (getLifecycleState() != RequestQueue::LifecycleState::Accepting) {
311 ERROR("OHCI: request queue did not enter the accepting state");
312 return;
313 }
314#endif
315
316// Dequeue main thread
317// new Thread(Processor::information().getCurrentThread()->getParent(),
318// threadStub, reinterpret_cast<void*>(this));
319
320// Install the IRQ handler
321#if X86_COMMON
322 m_IrqId = Machine::instance().getIrqManager()->registerPciIrqHandler(
323 static_cast<IrqHandler*>(this), this, IrqPolicy::pciIntxThreaded());
324 if (!m_IrqId) {
325 ERROR("OHCI: could not register the PCI interrupt callback");
326 return;
327 }
328 Machine::instance().getIrqManager()->control(
329 getInterruptNumber(), IrqManager::MitigationThreshold,
330 (1500000 / 64)); // 12KB/ms (12Mbps) in bytes, divided by 64 bytes
331 // maximum per transfer/IRQ
332#endif
333
334 // Get the number of ports and delay for power-up for this root hub.
335 uint32_t rhDescA = m_pBase->read32(OhciRhDescriptorA);
336 uint8_t powerWait = ((rhDescA >> 24) & 0xFF) * 2;
337 m_nPorts = rhDescA & 0xFF;
338
339 if (!UsbHcd::validOhciRootPortCount(m_nPorts)) {
340 ERROR("OHCI: unsupported root-port count " << Dec << m_nPorts << Hex);
341 m_nPorts = 0;
342 return;
343 }
344
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);
348 return;
349 }
350 }
351
352 DEBUG_LOG("USB: OHCI: Reset complete, " << Dec << m_nPorts << Hex << " ports available");
353
354 if (m_nPorts) {
355 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
356
357 // Establish a clean aggregate before the initial state scan. Changes
358 // after this flush remain pending until RHSC is enabled below.
359 m_pBase->write32(OhciInterruptRhStsChange, OhciInterruptStatus);
360 (void)m_pBase->read32(OhciInterruptStatus);
361
362 if (m_pBase->read32(OhciRhStatus) & OhciRhHubStsOverCurrentCh) {
363 m_pBase->write32(OhciRhHubStsOverCurrentCh, OhciRhStatus);
364 (void)m_pBase->read32(OhciRhStatus);
365 }
366
367 // The initial scan samples the current connection state directly, so
368 // stale change indications can be retired without losing that state.
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;
372 if (portChanges) {
373 m_pBase->write32(portChanges, portRegister);
374 (void)m_pBase->read32(portRegister);
375 }
376 }
377 }
378
379 // Transfer-completion sources become live only after IRQ registration,
380 // queue startup, and root-port token configuration have all succeeded.
381 m_pBase->write32(OhciInterruptOperational, OhciInterruptEnable);
382 (void)m_pBase->read32(OhciInterruptEnable);
383
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);
387
388 // Needs port power, do so
389 m_pBase->write32(OhciRhPortStsPower, OhciRhPortStatus + (i * 4));
390
391 // Wait as long as it needs
392 Time::delay(powerWait * Time::Multiplier::Millisecond);
393 }
394
395 DEBUG_LOG("OHCI: Determining if there's a device on this port");
396
397 // Check for a connected device
398 if (m_pBase->read32(OhciRhPortStatus + (i * 4)) & OhciRhPortStsConnected) {
399#if THREADS
400 // Device discovery constructs the controller under the global tree
401 // lock. The worker's topology mutation waits until the factory returns.
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");
405 }
406 m_PortChanges[i].acknowledge(observation.generation);
407#else
408 executeRequest(i);
409#endif
410 }
411 }
412
413#if THREADS
414 if (m_nPorts) {
415 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
416 m_RootHubStatusChangeDesired = true;
417 setRootHubStatusChangeSource(true);
418 }
419#endif
420
421 m_Initialised = true;
422}
423
424Ohci::~Ohci() {
425 // Quiesce only the root-port producer first. Transfer and SOF callbacks
426 // must remain live while an active enumeration request drains.
427 if (m_pBase) {
430 m_TeardownPhase = 1;
431 m_RootHubStatusChangeDesired = false;
433 }
434
435 // The RHSC mask and IRQ serialization above close and drain observe().
436 for (size_t i = 0; i < m_nPorts; ++i) {
437 m_PortChanges[i].stopAfterQuiesce();
438 }
440
441 // Enumeration is quiesced, while transfer cancellation and DMA are still
442 // live for class-driver destructors.
444
445 // No new descriptor builder can race the terminal scan. Calls which were
446 // already admitted finish while the controller and transfer IRQ are live.
449
451 uint32_t resetControl = 0;
452
453 if (m_pBase && m_pHcca) {
454 LockGuard<ControllerLock> controllerGuard(m_Mutex);
455
456 // MIE closes new hardware publications. Confirming USBSUSPEND
457 // establishes ownership of every ED and TD before reclamation.
458 const uint32_t control = m_pBase->read32(OhciControl);
459 resetControl = control & ~(OhciControlStateFunctionalMask | 0x3C);
460 m_pBase->write32(OhciInterruptMIE, OhciInterruptDisable);
461 transitionControllerState(resetControl | OhciControlStateSuspended,
462 "OHCI teardown suspend timed out after 100 ms");
463
464 {
466 m_TeardownPhase = 2;
468
469 // A captured completion normally waits for SOF. The controller is
470 // now suspended, so teardown itself owns that reclamation boundary.
471 while (true) {
472 ED* pED = nullptr;
473 {
475 if (m_DequeueList.count())
476 pED = m_DequeueList.popFront();
477 }
478 if (!pED)
479 break;
480 terminalizeEDForTeardown(pED, completions);
481 }
482
483 constexpr size_t EdCount = OhciDescriptorRegionBytes / sizeof(ED);
484 for (size_t i = 0; i < EdCount; ++i) {
485 if (m_ControlEDBitmap.test(i))
486 terminalizeEDForTeardown(&m_pControlEDList[i], completions);
487 if (m_BulkEDBitmap.test(i))
488 terminalizeEDForTeardown(&m_pBulkEDList[i], completions);
489 }
490
491 // Periodic callbacks are recurring events, not a one-shot terminal
492 // contract. Stop the subscription and reclaim it without inventing
493 // a final callback. Records already captured by an IRQ own snapshots
494 // and are drained by m_CallbackOperations below.
495 ED* pPeriodicDummy = m_pPeriodicEDList;
496 {
498 if (pPeriodicDummy) {
499 pPeriodicDummy->pNext = 0;
500 if (pPeriodicDummy->pMetaData) {
501 pPeriodicDummy->pMetaData->pPrev = pPeriodicDummy;
502 pPeriodicDummy->pMetaData->pNext = pPeriodicDummy;
503 }
504 m_pPeriodicQueueTail = pPeriodicDummy;
505 }
506 }
507 for (size_t i = 1; i < EdCount; ++i) {
508 if (!m_PeriodicEDBitmap.test(i))
509 continue;
510
511 ED* pED = &m_pPeriodicEDList[i];
513 pED->bSkip = true;
514 if (pED->pMetaData) {
515 pED->pMetaData->bIgnore = true;
516 pED->pMetaData->bLinked = false;
517 pED->pMetaData->pPrev = nullptr;
518 pED->pMetaData->pNext = nullptr;
519 }
520 pED->pNext = 0;
521 retireEDStorage(pED);
522 }
523
524 if (completions.count())
525 m_CompletionDeliveries.publish(completions);
526
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);
531
532 m_pBase->write32(0, OhciHcca);
533 m_pBase->write32(0, OhciControlHeadED);
534 m_pBase->write32(0, OhciBulkHeadED);
535 (void)m_pBase->read32(OhciHcca);
536 }
537 } else {
539 m_TeardownPhase = 2;
541 }
542
543#if X86_COMMON
544 if (m_IrqId) {
545 if (!Machine::instance().getIrqManager()->unregisterHandler(m_IrqId,
546 static_cast<IrqHandler*>(this))) {
547 panic(
548 "OHCI teardown could not synchronously unregister its IRQ "
549 "callback");
550 }
551 m_IrqId = 0;
552 }
553#endif
554
555 while (completions.count()) {
556 auto* completion = completions.popFront();
558 }
559
563
564 if (m_pPeriodicEDList && m_PeriodicEDBitmap.test(0)) {
565 LockGuard<ControllerLock> controllerGuard(m_Mutex);
566 retireEDStorage(m_pPeriodicEDList);
567 m_pPeriodicQueueTail = nullptr;
568 }
569
570 assert(m_CompletionDeliveries.empty());
571 assert(!m_DequeueList.count());
572 assert(!m_FullSchedule.count());
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));
581 }
582 constexpr size_t FinalTdCount = OhciDescriptorRegionBytes / sizeof(TD);
583 for (size_t i = 0; i < FinalTdCount; ++i)
584 assert(!m_TDBitmap.test(i));
585 assert(m_SubmissionOperations.isClosedAndDrained());
586 assert(m_CallbackOperations.isClosedAndDrained());
587 assert(m_AcceptedOperations.isClosedAndDrained());
588 assert(m_CancellationOperations.isClosedAndDrained());
589
590 if (m_pBase && m_pHcca) {
591 // Leave the host controller in USBRESET, with every schedule disabled.
592 m_pBase->write32(resetControl, OhciControl);
593 (void)m_pBase->read32(OhciControl);
594 m_pHcca = nullptr;
595 }
596}
597
599 m_pBase->write32(OhciInterruptRhStsChange, enabled ? OhciInterruptEnable : OhciInterruptDisable);
600 (void)m_pBase->read32(OhciInterruptEnable);
601}
602
603void Ohci::transitionControllerState(uint32_t control, const char* timeoutMessage) {
604 const uint32_t expected = control & OhciControlStateFunctionalMask;
605 m_pBase->write32(control, OhciControl);
606 (void)m_pBase->read32(OhciControl);
607
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);
612 }
613
614 if ((m_pBase->read32(OhciControl) & OhciControlStateFunctionalMask) != expected)
615 panic(timeoutMessage);
616}
617
618void Ohci::removeED(ED* pED) {
620
621 if (!pED || !pED->pMetaData)
622 return;
623
624#ifdef USB_VERBOSE_DEBUG
625 DEBUG_LOG("OHCI: removing ED #" << pED->pMetaData->id
626 << " from the schedule to prepare for reclamation");
627#endif
628
629 const Lists type = pED->pMetaData->edType;
630 detachED(pED);
631
632 // This list remains stopped until SOF establishes the reclamation boundary.
633 stop(type);
634
635 {
637 m_DequeueList.pushBack(pED);
638 }
639
640 // Clear any pending SOF interrupt and then enable the SOF IRQ.
641 m_pBase->write32(OhciInterruptStartOfFrame, OhciInterruptStatus);
642 m_pBase->write32(OhciInterruptStartOfFrame, OhciInterruptEnable);
643}
644
645void Ohci::detachED(ED* pED) {
646 if (!pED || !pED->pMetaData)
647 return;
648
649 pED->bSkip = true;
650 pED->pMetaData->bIgnore = true;
651
652 if (!pED->pMetaData->bLinked)
653 return;
654
655 ED* pPrev = pED->pMetaData->pPrev;
656 ED* pNext = pED->pMetaData->pNext;
657
658 ED** pQueueHead = 0;
659 ED** pQueueTail = 0;
660 Spinlock* pListLock = nullptr;
661
662 if (pED->pMetaData->edType == ControlList) {
663 pQueueHead = &m_pControlQueueHead;
664 pQueueTail = &m_pControlQueueTail;
665 pListLock = &m_ControlListChangeLock;
666 } else if (pED->pMetaData->edType == BulkList) {
667 pQueueHead = &m_pBulkQueueHead;
668 pQueueTail = &m_pBulkQueueTail;
669 pListLock = &m_BulkListChangeLock;
670 } else {
671 ERROR("OHCI: ED #" << pED->pMetaData->id << " has an invalid type!");
672 return;
673 }
674
675 LockGuard<Spinlock> listGuard(*pListLock);
676 bool bControl = pED->pMetaData->edType == ControlList;
677
678 // Unlink from the hardware linked list.
679 if (pED == *pQueueHead) {
680#ifdef USB_VERBOSE_DEBUG
681 DEBUG_LOG(
682 "OHCI: ED was a queue head, adjusting controller state "
683 "accordingly");
684#endif
685
686 *pQueueHead = pNext;
687
688 if (bControl)
689 m_pBase->write32(vtp_ed(pNext), OhciControlHeadED);
690 else
691 m_pBase->write32(vtp_ed(pNext), OhciBulkHeadED);
692 } else if (pPrev) {
693 pPrev->pNext = pED->pNext;
694 }
695
696 // Simply for tracking purposes, make sure the tail is valid.
697 if (pED == *pQueueTail) {
698 *pQueueTail = pPrev;
699 }
700
701 // Unlink from the software linked list.
702 if (pPrev)
703 pPrev->pMetaData->pNext = pNext;
704 if (pNext)
705 pNext->pMetaData->pPrev = pPrev;
706
707 pED->pMetaData->pPrev = nullptr;
708 pED->pMetaData->pNext = nullptr;
709 pED->pMetaData->bLinked = false;
710 pED->pNext = 0;
711}
712
714 LockGuard<Spinlock> scheduleGuard(m_ScheduleChangeLock);
715 for (List<ED*>::Iterator it = m_FullSchedule.begin(); it != m_FullSchedule.end();) {
716 if (*it == pED) {
717 m_FullSchedule.erase(it);
718 return;
719 }
720 ++it;
721 }
722}
723
726 for (List<ED*>::Iterator it = m_DequeueList.begin(); it != m_DequeueList.end();) {
727 if (*it == pED) {
728 m_DequeueList.erase(it);
729 return;
730 }
731 ++it;
732 }
733}
734
736 if (!pED || !pED->pMetaData)
737 return;
738
739 for (List<TD*>::Iterator it = pED->pMetaData->completedTdList.begin();
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);
744 }
745 pED->pMetaData->completedTdList.clear();
746
747 for (List<TD*>::Iterator it = pED->pMetaData->tdList.begin(); it != pED->pMetaData->tdList.end();
748 ++it) {
749 const size_t tdId = (*it)->id;
750 ByteSet(*it, 0, sizeof(TD));
751 m_TDBitmap.clear(tdId);
752 }
753 pED->pMetaData->tdList.clear();
754 pED->pMetaData->pFirstTD = nullptr;
755 pED->pMetaData->pLastTD = nullptr;
756}
757
759 if (!pED || !pED->pMetaData)
760 return;
761
763 ED::MetaData* metadata = pED->pMetaData;
764 const size_t id = metadata->id & OhciDescriptorOffsetMask;
765 const Lists type = metadata->edType;
766 const bool acceptedOperation = metadata->acceptedOperation;
767 delete metadata;
768 ByteSet(pED, 0, sizeof(ED));
769
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);
776
777 if (acceptedOperation)
779}
780
782 ED* pED, const UsbHcd::TransferCompletion::Claim& claim) {
783 if (!pED || !pED->pMetaData)
784 return nullptr;
785 assert(pED && pED->pMetaData);
786 assert(!pED->pMetaData->bPeriodic);
787 assert(claim.generation == pED->pMetaData->completion.generation());
788
790 auto* cleanup = new OhciCompletionCleanup{this, pED, claim.generation};
791 return m_CompletionDeliveries.create({pED->pMetaData->id, claim.generation}, claim.callback,
792 claim.parameter, claim.result, finishDeferredCompletion,
793 cleanup);
794}
795
798 if (!pED || !pED->pMetaData || pED->pMetaData->bPeriodic)
799 return;
800
802 detachED(pED);
804
805 if (pED->pMetaData->completion.state() == UsbHcd::TransferCompletion::State::Idle) {
806 retireEDStorage(pED);
807 return;
808 }
809
811 if (pED->pMetaData->completion.claimForTeardown(-TransactionError, claim)) {
812 completions.pushBack(prepareCompletion(pED, claim));
813 }
814}
815
816#if X86_COMMON
817IrqDisposition Ohci::irq(irq_id_t number) {
818 (void)number;
819
821 if (!m_CallbackOperations.tryAcquire(callback)) {
823 }
824
826 {
828
829 if (m_TeardownPhase == 2) {
831 }
832
833 if (!m_pHcca) {
834 // Assume not for us - no HCCA yet!
835 return IrqDisposition::NotHandled;
836 }
837
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;
842 }
843
844 // Not for us?
845 if (!nStatus) {
846 DEBUG_LOG("USB: OHCI: irq is not for us");
847 return IrqDisposition::NotHandled;
848 }
849
850 // However, make sure we do not get interrupted during handling.
851 m_pBase->write32(OhciInterruptMIE, OhciInterruptDisable);
852 (void)m_pBase->read32(OhciInterruptEnable);
853
854 // HCCA DoneHead belongs to software until WDH is acknowledged. Re-read
855 // it after closing MIE, then save and clear it before processing so the
856 // controller has an empty slot when WDH is retired below.
857 const uint32_t doneHead = m_pHcca->pDoneHead;
858 if (doneHead) {
859 m_pHcca->pDoneHead = 0;
860 FENCE();
861 nStatus |= OhciInterruptWbDoneHead;
862 if (doneHead & 0x1) {
863 nStatus |= m_pBase->read32(OhciInterruptStatus) & m_pBase->read32(OhciInterruptEnable);
864 }
865 }
866
867 // Clear the MIE bit from the interrupt status. We don't care for it.
868 nStatus &= ~OhciInterruptMIE;
869 bool sofDrained = true;
870 bool doneHeadDrained = true;
871
872#ifdef USB_VERBOSE_DEBUG
873 DEBUG_LOG("OHCI: IRQ " << nStatus);
874#endif
875
876 if (nStatus & OhciInterruptUnrecoverableError) {
878
879 // Don't enable interrupts again, controller is not in a safe state.
880 ERROR("OHCI: controller is hung!");
881 return IrqDisposition::Handled;
882 }
883
884 if (nStatus & OhciInterruptStartOfFrame) {
885#ifdef USB_VERBOSE_DEBUG
886 DEBUG_LOG("OHCI: SOF, preparing to reclaim EDs...");
887#endif
888
889 // Firstly disable the SOF interrupt now that we've gotten it.
890 m_pBase->write32(OhciInterruptStartOfFrame, OhciInterruptDisable);
891
892 // Process the reclaim list.
893 constexpr size_t EdListCount = 3 * (OhciDescriptorRegionBytes / sizeof(ED));
894 size_t reclaimBudget = EdListCount;
895 while (reclaimBudget) {
896 ED* pED = nullptr;
897 {
899 if (m_DequeueList.count())
900 pED = m_DequeueList.popFront();
901 else
902 break;
903 }
904
905 --reclaimBudget;
906 if (pED) {
907 const Lists type = pED->pMetaData->edType;
909 const bool ownsPublication = pED->pMetaData->completion.claimCaptured(claim);
910
911#ifdef USB_VERBOSE_DEBUG
912 DEBUG_LOG("OHCI: freeing ED #" << pED->pMetaData->id << ".");
913#endif
914
915 if (ownsPublication)
916 completions.pushBack(prepareCompletion(pED, claim));
917
918 // Safe to restore this list to the running state.
920 start(type);
921 }
922 }
923
924 {
926 if (m_DequeueList.count()) {
927 sofDrained = false;
928 ERROR_NOLOCK("OHCI: exceeded the SOF reclaim scan budget");
929 }
930 }
931 }
932
933 // Check for newly connected / disconnected devices. A threadless build
934 // leaves RHSC masked because enumeration can block and allocate.
935#if THREADS
936 if (nStatus & OhciInterruptRhStsChange) {
938
939 // Clear and flush the aggregate before scanning. A change after its
940 // port has been scanned will relatch RHSC and cannot be erased by a
941 // trailing aggregate acknowledgement.
942 m_pBase->write32(OhciInterruptRhStsChange, OhciInterruptStatus);
943 (void)m_pBase->read32(OhciInterruptStatus);
944
945 if (m_pBase->read32(OhciRhStatus) & OhciRhHubStsOverCurrentCh) {
946 m_pBase->write32(OhciRhHubStsOverCurrentCh, OhciRhStatus);
947 (void)m_pBase->read32(OhciRhStatus);
948 }
949
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);
955
956 // A reset worker masks RHSC before issuing reset and owns PRSC
957 // until it has sampled and cleared completion. A stale PRSC
958 // with no owner can be retired here instead of causing an IRQ
959 // storm.
960 if ((portStatus & OhciRhPortStsResCh) && !m_PortResetActive) {
961 acknowledgeMask |= OhciRhPortStsResCh;
962 }
963
964 if (portStatus & OhciRhPortStsConnStsCh) {
965 const bool deferred = deferConnectionChangeIfSuppressed(i);
966 bool acknowledge = deferred;
967 size_t generation = 0;
968 if (!deferred) {
969 const auto observation = m_PortChanges[i].observe();
970 acknowledge = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
971 assert(acknowledge);
972 if (acknowledge) {
973 generation = observation.generation;
974 m_DeferredPortChanges.defer(i, generation);
975 }
976 }
977
978 if (acknowledge) {
979 acknowledgeMask |= OhciRhPortStsConnStsCh;
980 } else {
981 // A configured preallocated token has no fallible
982 // admission path while the queue is accepting. Preserve
983 // CSC for diagnosis, but mask RHSC to avoid a
984 // level-triggered IRQ livelock if that invariant is
985 // ever violated.
986 m_RootHubStatusChangeDesired = false;
988 }
989 }
990
991 if (acknowledgeMask) {
992 // OHCI root-port command bits alias the readable status
993 // bits; writing only upper change bits avoids replaying
994 // commands.
995 m_pBase->write32(acknowledgeMask, portRegister);
996 (void)m_pBase->read32(portRegister);
997 }
998
999 const size_t generation = m_DeferredPortChanges.release(i);
1000 if (generation) {
1001 m_PortChanges[i].acknowledge(generation);
1002 }
1003 }
1004 }
1005#endif
1006
1007 // A list of EDs that persist in the schedule. Used to repopulate the
1008 // schedule list.
1009 List<ED*> persistList;
1010
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;
1015 ED* pED = 0;
1016 while (scheduleBudget) {
1017 --scheduleBudget;
1018 {
1019 LockGuard<Spinlock> guard(m_ScheduleChangeLock);
1020 if (m_FullSchedule.count())
1021 pED = m_FullSchedule.popFront();
1022 else
1023 break;
1024 }
1025
1026 // Assume not yet linked properly
1027 if (pED->pMetaData->bIgnore) {
1028 persistList.pushBack(pED);
1029 continue;
1030 }
1031
1032 bool bPeriodic = pED->pMetaData->bPeriodic;
1033
1034 // Iterate the TD list
1035 TD* pTD = 0;
1036 size_t tdBudget = TdListCount;
1037 while (pED->pMetaData->tdList.count() && tdBudget) {
1038 --tdBudget;
1039 pTD = pED->pMetaData->tdList.popFront();
1040
1041 // TD not yet handled - return to the list and go to the
1042 // next ED.
1043 if (pTD->nStatus == 0xF) {
1044 pED->pMetaData->tdList.pushFront(pTD);
1045 break;
1046 }
1047
1048 ssize_t nResult;
1049 if (pTD->nStatus) {
1050#ifdef USB_VERBOSE_DEBUG
1051 if (!bPeriodic)
1052 ERROR_NOLOCK("TD Error " << Dec << pTD->nStatus << Hex);
1053#endif
1054 nResult = -pTD->getError();
1055 } else {
1056 if (pTD->pBufferStart) {
1057 // Only a part of the buffer has been transfered
1058 size_t nBytesLeft = pTD->pBufferEnd - pTD->pBufferStart + 1;
1059 nResult = pTD->nBufferSize - nBytesLeft;
1060 } else
1061 nResult = pTD->nBufferSize;
1062 pED->pMetaData->nTotalBytes += nResult;
1063 }
1064#ifdef USB_VERBOSE_DEBUG
1065 DEBUG_LOG_NOLOCK(
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);
1071#endif
1072
1074 bool bEndOfTransfer =
1075 (!bPeriodic && ((nResult < 0) || (pTD == pED->pMetaData->pLastTD))) ||
1076 (bPeriodic && (nResult >= 0));
1077
1078 if (!bPeriodic)
1079 pED->pMetaData->completedTdList.pushBack(pTD);
1080
1081 // Last TD or error condition, if async, otherwise only when
1082 // it gives no error
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);
1087
1088 if (!bPeriodic && ownsCompletion) {
1089 removeED(pED);
1090 continue;
1091 } else if (bPeriodic) {
1092 // Invert data toggle
1093 pTD->bDataToggle = !pTD->bDataToggle;
1094
1095 // Clear the total bytes field so it won't grow with
1096 // each completed transfer
1097 pED->pMetaData->nTotalBytes = 0;
1098 }
1099
1100 if (bPeriodic && pED->pMetaData->pCallback) {
1101 completions.pushBack(m_CompletionDeliveries.create(
1102 {pED->pMetaData->id, m_CompletionDeliveries.nextGeneration()},
1103 pED->pMetaData->pCallback, pED->pMetaData->pParam, completionResult));
1104 }
1105 }
1106
1107 // Interrupt TDs need to be always active
1108 if (bPeriodic) {
1109 pTD->nStatus = 0xf;
1110 pTD->pBufferStart = pTD->pBufferEnd - pTD->nBufferSize + 1;
1111 pED->pHeadTD = PHYS_TD(pTD->id) >> 4;
1112
1113 pED->pMetaData->tdList.pushBack(pTD);
1114 break; // Only one TD in a periodic transfer.
1115 }
1116 }
1117
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");
1122 }
1123
1124 // If this ED is not queued for deletion, make sure we can use
1125 // it in the next IRQ.
1126 if (!pED->pMetaData->bIgnore)
1127 persistList.pushBack(pED);
1128 }
1129
1130 {
1131 LockGuard<Spinlock> guard(m_ScheduleChangeLock);
1132 if (m_FullSchedule.count()) {
1133 doneHeadDrained = false;
1134 ERROR_NOLOCK("OHCI: exceeded the done-head ED scan budget");
1135 }
1136 }
1137 }
1138
1139 // Restore EDs into the schedule if they were removed and need to
1140 // persist.
1141 if (persistList.count()) {
1142 LockGuard<Spinlock> guard(m_ScheduleChangeLock);
1143 for (List<ED*>::Iterator it = persistList.begin(); it != persistList.end();) {
1144 m_FullSchedule.pushBack(*it);
1145 it = persistList.erase(it);
1146 }
1147 }
1148
1149 // RHSC was acknowledged before its scan so a later port edge cannot be
1150 // erased here.
1151 uint32_t acknowledgeStatus = nStatus & ~OhciInterruptRhStsChange;
1152 if (!sofDrained) {
1153 acknowledgeStatus &= ~OhciInterruptStartOfFrame;
1154 m_pBase->write32(OhciInterruptStartOfFrame, OhciInterruptEnable);
1155 }
1156 if (!doneHeadDrained) {
1157 acknowledgeStatus &= ~OhciInterruptWbDoneHead;
1158 }
1159 if (acknowledgeStatus) {
1160 m_pBase->write32(acknowledgeStatus, OhciInterruptStatus);
1161 (void)m_pBase->read32(OhciInterruptStatus);
1162 }
1163
1164 if (m_TeardownPhase < 2) {
1165 m_pBase->write32(OhciInterruptMIE, OhciInterruptEnable);
1166 }
1167
1168 if (completions.count())
1169 m_CompletionDeliveries.publish(completions);
1170 }
1171
1172 while (completions.count()) {
1173 auto* completion = completions.popFront();
1174 m_CompletionDeliveries.deliver(completion);
1175 }
1176
1177 return IrqDisposition::Handled;
1178}
1179#endif
1180
1181void Ohci::addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid,
1182 uintptr_t pBuffer, size_t nBytes) {
1183 OperationBarrier::Lease submission;
1184 if (!m_SubmissionOperations.tryAcquire(submission))
1185 return;
1186
1187 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1189
1190 constexpr size_t EdCount = OhciDescriptorRegionBytes / sizeof(ED);
1191 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1192 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1193 ED* pED = nullptr;
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;
1204 } else
1205 valid = false;
1206
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.");
1211 return;
1212 }
1213
1214 size_t nIndex = m_TDBitmap.getFirstClear();
1215 if (nIndex >= (OhciDescriptorRegionBytes / sizeof(TD))) {
1216 ERROR("USB: OHCI: TD space full");
1217 pED->pMetaData->bBuildFailed = true;
1218 return;
1219 }
1220 m_TDBitmap.set(nIndex);
1221
1222 // Grab the TD pointer we're going to set up now
1223 TD* pTD = &m_pTDList[nIndex];
1224 ByteSet(pTD, 0, sizeof(TD));
1225 pTD->id = nIndex;
1226
1227 // Buffer rounding - allow packets smaller than the buffer we specify
1228 pTD->bBuffRounding = 1;
1229
1230 // PID for the transfer
1231 switch (pid) {
1232 case UsbPidSetup:
1233 pTD->nPid = 0;
1234 break;
1235 case UsbPidOut:
1236 pTD->nPid = 1;
1237 break;
1238 case UsbPidIn:
1239 pTD->nPid = 2;
1240 break;
1241 default:
1242 pTD->nPid = 3;
1243 };
1244
1245 // Active
1246 pTD->nStatus = 0xf;
1247 (void)bToggle;
1248
1249 // Buffer for transfer
1250 if (nBytes) {
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);
1257 return;
1258 }
1259
1260 // A General TD can name the current and final physical 4 KiB pages. It
1261 // cannot describe a third page, regardless of the target VM page size.
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);
1272 return;
1273 }
1274
1275 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
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);
1285 return;
1286 }
1287
1288 // Completion and periodic rearming use the controller-updated current
1289 // buffer pointer as a linear byte cursor.
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);
1296 return;
1297 }
1298
1299 pTD->pBufferStart = physicalStart;
1300 pTD->pBufferEnd = physicalEnd;
1301 pTD->nBufferSize = nBytes;
1302 }
1303
1304 // This is the last TD so far
1305 pTD->bLast = true;
1306
1307 // Add our TD to the ED's queue.
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;
1312 } else {
1313 pED->pMetaData->pFirstTD = pTD;
1314 pED->pHeadTD = PHYS_TD(nIndex) >> 4;
1315 }
1316 pED->pMetaData->pLastTD = pTD;
1317
1318 pED->pMetaData->tdList.pushBack(pTD);
1319}
1320
1321uintptr_t Ohci::createTransaction(UsbEndpoint endpointInfo) {
1322 OperationBarrier::Lease submission;
1323 if (!m_SubmissionOperations.tryAcquire(submission))
1324 return static_cast<uintptr_t>(-1);
1325
1326 // Determine what kind of transaction this is.
1327 bool bIsBulk = endpointInfo.nEndpoint > 0;
1328
1329 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1331
1332 ED* pED = nullptr;
1333 size_t nIndex = bIsBulk ? m_BulkEDBitmap.getFirstClear() : m_ControlEDBitmap.getFirstClear();
1334
1335 if (nIndex >= (OhciDescriptorRegionBytes / sizeof(ED))) {
1336 ERROR("USB: OHCI: ED space full");
1337 return static_cast<uintptr_t>(-1);
1338 }
1339
1340 if (bIsBulk) {
1341 m_BulkEDBitmap.set(nIndex);
1342 pED = &m_pBulkEDList[nIndex];
1343 nIndex += OhciDescriptorRegionBytes;
1344 } else {
1345 m_ControlEDBitmap.set(nIndex);
1346 pED = &m_pControlEDList[nIndex];
1347 }
1348
1349 ByteSet(pED, 0, sizeof(ED));
1350
1351 // Device address, endpoint and speed
1352 pED->nAddress = endpointInfo.nAddress;
1353 pED->nEndpoint = endpointInfo.nEndpoint;
1354 pED->bLoSpeed = endpointInfo.speed == LowSpeed;
1355
1356 // Maximum packet size
1357 pED->nMaxPacketSize = endpointInfo.nMaxPacketSize;
1358
1359 // Make sure this ED is ignored until it's properly queued.
1360 pED->bSkip = true;
1361
1362 // Setup the metadata
1363 pED->pMetaData = new ED::MetaData;
1364 pED->pMetaData->endpointInfo = endpointInfo;
1365 pED->pMetaData->id = nIndex;
1366 pED->pMetaData->bIgnore = true; // Don't handle this ED until we're ready.
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;
1374 pED->pMetaData->pCallback = nullptr;
1375 pED->pMetaData->pParam = 0;
1376 pED->pMetaData->acceptedOperation = false;
1377
1378 // Complete
1379 return nIndex;
1380}
1381
1382bool Ohci::doAsync(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
1383 uintptr_t pParam) {
1384 OperationBarrier::Lease submission;
1385 if (!m_SubmissionOperations.tryAcquire(submission))
1386 return false;
1387
1388 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1390
1391 // pTransaction will be 0x0xxx for CONTROL, 0x1xxx for BULK, 0x2xxx for
1392 // PERIODIC.
1393 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1394 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1395 constexpr size_t EdCount = OhciDescriptorRegionBytes / sizeof(ED);
1396
1397 Spinlock* pLock = nullptr;
1398 ED* pED = nullptr;
1399 bool valid = edOffset < EdCount;
1400 if (valid && transactionType == 0) {
1401 valid = m_ControlEDBitmap.test(edOffset);
1402 pED = valid ? &m_pControlEDList[edOffset] : nullptr;
1403 pLock = &m_ControlListChangeLock;
1404 } else if (valid && transactionType == 1) {
1405 valid = m_BulkEDBitmap.test(edOffset);
1406 pED = valid ? &m_pBulkEDList[edOffset] : nullptr;
1407 pLock = &m_BulkListChangeLock;
1408 } else
1409 valid = false;
1410
1411 if (pTransaction == static_cast<uintptr_t>(-1) || !valid) {
1412 ERROR("OHCI: doAsync: didn't get a valid transaction id [" << pTransaction << ", " << edOffset
1413 << "].");
1414 return false;
1415 }
1416
1417 if (!pED->pMetaData) {
1418 ERROR("OHCI: doAsync: transaction metadata is missing");
1419 return false;
1420 }
1421
1422 if (pED->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle) {
1423 ERROR("OHCI: doAsync: transaction is already accepted");
1424 return false;
1425 }
1426
1427 if (pED->pMetaData->bBuildFailed || !pED->pMetaData->pLastTD) {
1428 ERROR("OHCI: doAsync: transaction could not be submitted [" << pTransaction << "].");
1429 retireEDStorage(pED);
1430 return false;
1431 }
1432
1434 retireEDStorage(pED);
1435 return false;
1436 }
1437 pED->pMetaData->acceptedOperation = true;
1438
1439 const bool bControl = transactionType == 0;
1440
1441 // Link the ED while it is still skipped. IRQ completion, cancellation and
1442 // teardown are all excluded by m_IrqProcessingLock until the final unskip.
1443 pLock->acquire();
1444
1445 // Always at the end of the ED queue. Zero means "no next ED" to OHCI.
1446 pED->pNext = 0;
1447
1448 // Handle the case where there is not yet a queue head.
1449 if (bControl) {
1450 if (!m_pControlQueueHead) {
1451#ifdef USB_VERBOSE_DEBUG
1452 DEBUG_LOG("OHCI: ED is now the control queue head.");
1453#endif
1454 m_pControlQueueHead = pED;
1455 }
1456 } else {
1457 if (!m_pBulkQueueHead) {
1458#ifdef USB_VERBOSE_DEBUG
1459 DEBUG_LOG("OHCI: ED is now the control queue head.");
1460#endif
1461 m_pBulkQueueHead = pED;
1462 }
1463 }
1464
1465 // Grab the queue head.
1466 ED* pQueueHead = nullptr;
1467 physical_uintptr_t queueHeadPhys = 0;
1468 if (bControl) {
1469 pQueueHead = m_pControlQueueHead;
1470 queueHeadPhys = vtp_ed(pQueueHead);
1471 } else {
1472 pQueueHead = m_pBulkQueueHead;
1473 queueHeadPhys = vtp_ed(pQueueHead);
1474 }
1475
1476 // Update the head of the relevant list.
1477 if (queueHeadPhys == vtp_ed(pED)) {
1478 if (bControl) {
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));
1483#endif
1484 m_pBase->write32(queueHeadPhys, OhciControlHeadED);
1485 } else {
1486#ifdef USB_VERBOSE_DEBUG
1487 DEBUG_LOG("OHCI: new bulk queue head is " << queueHeadPhys);
1488#endif
1489 m_pBase->write32(queueHeadPhys, OhciBulkHeadED);
1490 }
1491 }
1492
1493 // Grab the current tail of the list and update it to point to us.
1494 ED* pTail = nullptr;
1495 if (bControl) {
1496 pTail = m_pControlQueueTail;
1497 m_pControlQueueTail = pED;
1498 } else {
1499 pTail = m_pBulkQueueTail;
1500 m_pBulkQueueTail = pED;
1501 }
1502
1503 // Point the old tail to this ED.
1504 if (pTail) {
1505 pTail->pNext = vtp_ed(pED) >> 4;
1506 pTail->pMetaData->pNext = pED;
1507 }
1508
1509 // Fix up the software linked list.
1510 pED->pMetaData->pNext = nullptr;
1511 pED->pMetaData->pPrev = pTail;
1512 pQueueHead->pMetaData->pPrev = nullptr;
1513 pED->pMetaData->bLinked = true;
1514
1515 pLock->release();
1516
1517 {
1518 LockGuard<Spinlock> scheduleGuard(m_ScheduleChangeLock);
1519 m_FullSchedule.pushBack(pED);
1520 }
1521
1522 // Arming immediately before unskip makes the callback obligation and the
1523 // hardware publication one indivisible commit to every competing path.
1524 pED->pMetaData->completion.arm(pCallback, pParam, m_CompletionDeliveries.nextGeneration());
1525 FENCE();
1526 pED->bSkip = pED->pMetaData->bIgnore = false;
1527
1528 // Restart the controller if it was stopped for some reason.
1529 start(pED->pMetaData->edType);
1530
1531 // Tell the controller that the list has valid TD in it now.
1532 // The OHCI will automatically stop processing the ED list if it determines
1533 // no more transfers are pending.
1534 uint32_t status = m_pBase->read32(OhciCommandStatus);
1535 status |= bControl ? OhciCommandControlListFilled : OhciCommandBulkListFilled;
1536 m_pBase->write32(status, OhciCommandStatus);
1537 return true;
1538}
1539
1540void Ohci::cancelAsyncAndDrain(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
1541 uintptr_t pParam) {
1542 OperationBarrier::Lease cancellation;
1543 if (!m_CancellationOperations.tryAcquire(cancellation) || !m_pBase)
1544 return;
1545
1546 bool drainDelivery = false;
1547 UsbHcd::CallbackDeliveryQueue::Key deliveryKey = {0, 0};
1549 ED* pED = nullptr;
1550
1551 {
1552 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1553
1554 // Confirm USBSUSPEND before treating the controller's EDs and TDs as
1555 // software-owned.
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");
1561
1562 bool restoreController = false;
1563
1564 {
1566
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;
1577 } else
1578 valid = false;
1579
1580 Lists completedList = ControlList;
1581 if (valid && pED && pED->pMetaData) {
1583 const auto disposition = pED->pMetaData->completion.claimCancellation(
1584 pCallback, pParam, -TransactionError, claim);
1585 if (disposition == UsbHcd::TransferCompletion::CancellationDisposition::Claimed) {
1586 completedList = pED->pMetaData->edType;
1588 detachED(pED);
1590 completions.pushBack(prepareCompletion(pED, claim));
1591 m_CompletionDeliveries.publish(completions);
1592 } else if (disposition ==
1593 UsbHcd::TransferCompletion::CancellationDisposition::DrainPublished) {
1594 deliveryKey = {pTransaction, claim.generation};
1595 drainDelivery = true;
1596 }
1597
1598 // A natural completion may have stopped its list while waiting
1599 // for SOF. Cancellation supplied that DMA boundary itself.
1600 if (completions.count())
1601 savedControl |= static_cast<uint32_t>(completedList);
1602 }
1603
1604 restoreController = m_TeardownPhase < 2;
1605 if (!restoreController) {
1606 m_pBase->write32(OhciInterruptAll, OhciInterruptDisable);
1607 (void)m_pBase->read32(OhciInterruptEnable);
1608 }
1609 }
1610
1611 if (restoreController) {
1612 // This may restore USBOPERATIONAL or USBRESUME. The state poll is
1613 // outside IRQ serialization so a slow controller cannot hold up
1614 // an interrupt path.
1615 transitionControllerState(savedControl, "OHCI cancellation restore timed out after 100 ms");
1616
1618 m_pBase->write32(OhciInterruptMIE, OhciInterruptEnable);
1619 (void)m_pBase->read32(OhciInterruptEnable);
1620 }
1621 }
1622
1623 while (completions.count()) {
1624 auto* completion = completions.popFront();
1625 m_CompletionDeliveries.deliver(completion);
1626 }
1627 if (drainDelivery)
1628 (void)m_CompletionDeliveries.drain(deliveryKey);
1629}
1630
1632 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
1633 bool producerAlreadyStopped) {
1634 (void)token;
1635 (void)callback;
1636 (void)parameter;
1637 (void)producerAlreadyStopped;
1638 panic("OHCI returned an interrupt-IN handle for an unsupported transfer");
1639 return false;
1640}
1641
1642bool Ohci::addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes,
1643 void (*pCallback)(uintptr_t, ssize_t),
1644 UsbInterruptInHandle& handle, uintptr_t pParam) {
1645 (void)endpointInfo;
1646 (void)pBuffer;
1647 (void)nBytes;
1648 (void)pCallback;
1649 (void)handle;
1650 (void)pParam;
1651 // The legacy OHCI path never built the periodic TD it claimed to publish.
1652 // Failing here retains neither a DMA buffer nor a callback target.
1653 WARNING("USB: OHCI: recurring interrupt-IN transfers are not implemented");
1654 return false;
1655}
1656
1658#if THREADS
1659 if (port >= m_nPorts) {
1660 ERROR("OHCI: invalid suppressed root-port replay " << Dec << port);
1661 return;
1662 }
1663
1665 // Teardown stops publication before its active enumeration worker returns.
1666 if (m_TeardownPhase) {
1667 return;
1668 }
1669 const auto observation = m_PortChanges[port].observe();
1670 const bool accepted = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
1671 if (accepted) {
1672 m_PortChanges[port].acknowledge(observation.generation);
1673 return;
1674 }
1675
1676 m_TeardownPhase = 1;
1677 {
1678 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
1679 m_RootHubStatusChangeDesired = false;
1681 }
1682 ERROR("OHCI: live suppressed root-port replay could not be published");
1683 assert(false);
1684#else
1685 (void)port;
1686#endif
1687}
1688
1689bool Ohci::portReset(uint8_t nPort, bool bErrorResponse) {
1691
1692 if (nPort >= m_nPorts) {
1693 return false;
1694 }
1695
1696#if THREADS
1698#endif
1699 const size_t portRegister = OhciRhPortStatus + (nPort * 4);
1700
1701 {
1702 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
1703
1704 // PRSC is level-signalled through RHSC. Mask the source while reset is
1705 // in flight so the worker that must clear PRSC cannot be starved by a
1706 // same-core interrupt loop.
1707 m_PortResetActive = true;
1709
1710 // Root-port lower bits are write commands, not an RMW-safe control
1711 // image. Writing only SetPortReset cannot echo unrelated W1C bits.
1712 m_pBase->write32(OhciRhPortStsReset, portRegister);
1713 (void)m_pBase->read32(portRegister);
1714 }
1715
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;
1721 break;
1722 }
1723 if (m_TeardownPhase) {
1724 break;
1725 }
1726 Time::delay(5 * Time::Multiplier::Millisecond);
1727 }
1728
1729 {
1730 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
1731
1732 // The reset worker exclusively owns PRSC while RHSC is masked. A
1733 // completion that arrived at the timeout boundary is still retired.
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);
1739 }
1740
1741 // SetPortEnable is also a command bit; do not echo the status image.
1742 if (resetComplete && !(m_pBase->read32(portRegister) & OhciRhPortStsEnable)) {
1743 m_pBase->write32(OhciRhPortStsEnable, portRegister);
1744 (void)m_pBase->read32(portRegister);
1745 }
1746
1747 m_PortResetActive = false;
1748 if (m_RootHubStatusChangeDesired) {
1749 // Any CSC that arrived during reset remained set while the source
1750 // was masked and becomes deliverable again here.
1752 }
1753 }
1754
1755 if (!resetComplete && !m_TeardownPhase) {
1756 ERROR("OHCI: timed out resetting root port " << nPort);
1757 }
1758 return resetComplete;
1759}
1760
1761uint64_t Ohci::executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5,
1762 uint64_t p6, uint64_t p7, uint64_t p8) {
1763 if (p1 >= m_nPorts) {
1764 return 0;
1765 }
1766 UsbHcd::PortChangeRequest::Completion completion(m_PortChanges[p1], static_cast<size_t>(p8));
1767 if (!completion) {
1768 return 0;
1769 }
1770
1771 // Check for a connected device
1772 if (m_pBase->read32(OhciRhPortStatus + (p1 * 4)) & OhciRhPortStsConnected) {
1773 if (!portReset(p1))
1774 return 0;
1775
1776 // Determine the speed of the attached device
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");
1780 deviceConnected(p1, LowSpeed);
1781 } else {
1782 DEBUG_LOG("USB: OHCI: Port " << Dec << p1 << Hex
1783 << " has a full-speed device connected to it");
1784 deviceConnected(p1, FullSpeed);
1785 }
1786 } else
1788 return 0;
1789}
1790
1791void Ohci::cancelRequest(const Request& request) {
1792 if (request.p1 < m_nPorts) {
1793 m_PortChanges[request.p1].cancel(static_cast<size_t>(request.p8));
1794 }
1795}
1796
1797void Ohci::stop(Lists list) {
1798 if (!m_pHcca)
1799 return;
1800
1801 uint32_t control = m_pBase->read32(OhciControl);
1802 control &= ~static_cast<int>(list);
1803 m_pBase->write32(control, OhciControl);
1804}
1805
1806void Ohci::start(Lists list) {
1807 if (!m_pHcca)
1808 return;
1809
1810 uint32_t control = m_pBase->read32(OhciControl);
1811 control |= static_cast<int>(list);
1812 m_pBase->write32(control, OhciControl);
1813}
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 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:39
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
Definition Ohci.h:54
void cancelRequest(const Request &request) override
Definition Ohci.cc:1791
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 Ohci.cc:1181
IrqDisposition irq(irq_id_t number) override
IRQ handler.
Definition Ohci.cc:817
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
Definition Ohci.cc:1382
void terminalizeEDForTeardown(ED *pED, List< UsbHcd::CallbackDeliveryQueue::Record * > &completions)
Definition Ohci.cc:796
MUST_USE_RESULT bool cancelInterruptInAndDrain(const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter, bool producerAlreadyStopped) override
Definition Ohci.cc:1631
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
Definition Ohci.cc:1321
OperationBarrier m_CancellationOperations
Definition Ohci.h:450
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 Ohci.cc:1761
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
Definition Ohci.cc:1689
void transitionControllerState(uint32_t control, const char *timeoutMessage)
Definition Ohci.cc:603
Spinlock m_ControlListChangeLock
Lock for changing the control list.
Definition Ohci.h:396
void detachED(ED *pED)
Definition Ohci.cc:645
void removeFromFullSchedule(ED *pED)
Definition Ohci.cc:713
void reclaimTransferDescriptors(ED *pED)
Definition Ohci.cc:735
Spinlock m_PeriodicListChangeLock
Lock for changing the periodic list.
Definition Ohci.h:393
Spinlock m_BulkListChangeLock
Lock for changing the bulk list.
Definition Ohci.h:399
IrqProcessingLock m_IrqProcessingLock
Lock for modifying the schedule list itself (m_FullSchedule)
Definition Ohci.h:379
Lists
Enumeration of lists that can be stopped or started.
Definition Ohci.h:74
void stop(Lists list)
Stops the controller from processing the given list.
Definition Ohci.cc:1797
OperationBarrier m_AcceptedOperations
Definition Ohci.h:453
UsbHcd::CallbackDeliveryQueue::Record * prepareCompletion(ED *pED, const UsbHcd::TransferCompletion::Claim &claim)
Definition Ohci.cc:781
void setRootHubStatusChangeSource(bool enabled)
Definition Ohci.cc:598
List< ED * > m_FullSchedule
Definition Ohci.h:433
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 Ohci.cc:1642
void removeED(ED *pED)
Detaches an ED and queues it for reclamation at the next frame.
Definition Ohci.cc:618
UsbHcd::CallbackDeliveryQueue m_CompletionDeliveries
Definition Ohci.h:382
OperationBarrier m_CallbackOperations
Definition Ohci.h:444
Spinlock m_RootHubLock
Serializes root-hub register access between reset and IRQ paths.
Definition Ohci.h:385
Spinlock m_DequeueListLock
Dequeue list lock.
Definition Ohci.h:429
OperationBarrier m_SubmissionOperations
Definition Ohci.h:447
void retireEDStorage(ED *pED)
Definition Ohci.cc:758
void replaySuppressedConnectionChange(size_t port) override
Definition Ohci.cc:1657
void removeFromDequeueList(ED *pED)
Definition Ohci.cc:724
List< ED * > m_DequeueList
List of EDs ready for dequeue (reclaiming)
Definition Ohci.h:436
ControllerLock m_PortResetMutex
Definition Ohci.h:373
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
Definition Ohci.cc:1540
ControllerLock m_Mutex
Global lock.
Definition Ohci.h:369
void start(Lists list)
Starts processing of the given list.
Definition Ohci.cc:1806
physical_uintptr_t vtp_ed(ED *pED)
Converts a software ED pointer to a physical address.
Definition Ohci.h:264
MUST_USE_RESULT bool tryAcquire(Lease &lease)
MUST_USE_RESULT bool tryEnter()
uint32_t readConfigSpace(Device *pDev, uint8_t offset)
Definition Pci.cc:83
void writeConfigSpace(Device *pDev, uint8_t offset, uint32_t data)
Definition Pci.cc:94
static PhysicalMemoryManager & instance()
static ProcessorInformation & information()
virtual void destroy()
void release()
Definition Spinlock.cc:161
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:35
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
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.
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 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
@ Dec
Definition Log.h:144
@ Hex
Definition Log.h:142
IrqDisposition
Definition IrqHandler.h:31
Iterator erase(Iterator &Iter)
Definition List.h:352
T popFront()
Definition List.h:330
void clear()
Definition List.h:399
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
void(* pCallback)(uintptr_t, ssize_t)
Definition Ohci.h:141