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 EMIT_IF(UsbVerboseDebug) {
625 DEBUG_LOG("OHCI: removing ED #" << pED->pMetaData->id
626 << " from the schedule to prepare for reclamation");
627 }
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 EMIT_IF(UsbVerboseDebug) {
681 DEBUG_LOG(
682 "OHCI: ED was a queue head, adjusting controller state "
683 "accordingly");
684 }
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 EMIT_IF(UsbVerboseDebug) {
873 DEBUG_LOG("OHCI: IRQ " << nStatus);
874 }
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 EMIT_IF(UsbVerboseDebug) {
886 DEBUG_LOG("OHCI: SOF, preparing to reclaim EDs...");
887 }
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 EMIT_IF(UsbVerboseDebug) {
912 DEBUG_LOG("OHCI: freeing ED #" << pED->pMetaData->id << ".");
913 }
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 EMIT_IF(UsbVerboseDebug) {
1051 if (!bPeriodic)
1052 ERROR_NOLOCK("TD Error " << Dec << pTD->nStatus << Hex);
1053 }
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 EMIT_IF(UsbVerboseDebug) {
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
1069 ? "OUT"
1070 : (pTD->nPid == 2 ? "IN" : (pTD->nPid == 0 ? "SETUP" : "")))
1071 << " " << nResult << Hex);
1072 }
1073
1075 bool bEndOfTransfer =
1076 (!bPeriodic && ((nResult < 0) || (pTD == pED->pMetaData->pLastTD))) ||
1077 (bPeriodic && (nResult >= 0));
1078
1079 if (!bPeriodic)
1080 pED->pMetaData->completedTdList.pushBack(pTD);
1081
1082 // Last TD or error condition, if async, otherwise only when
1083 // it gives no error
1084 if (bEndOfTransfer) {
1085 const ssize_t completionResult = nResult < 0 ? nResult : pED->pMetaData->nTotalBytes;
1086 const bool ownsCompletion =
1087 bPeriodic || pED->pMetaData->completion.captureNatural(completionResult);
1088
1089 if (!bPeriodic && ownsCompletion) {
1090 removeED(pED);
1091 continue;
1092 } else if (bPeriodic) {
1093 // Invert data toggle
1094 pTD->bDataToggle = !pTD->bDataToggle;
1095
1096 // Clear the total bytes field so it won't grow with
1097 // each completed transfer
1098 pED->pMetaData->nTotalBytes = 0;
1099 }
1100
1101 if (bPeriodic && pED->pMetaData->pCallback) {
1102 completions.pushBack(m_CompletionDeliveries.create(
1103 {pED->pMetaData->id, m_CompletionDeliveries.nextGeneration()},
1104 pED->pMetaData->pCallback, pED->pMetaData->pParam, completionResult));
1105 }
1106 }
1107
1108 // Interrupt TDs need to be always active
1109 if (bPeriodic) {
1110 pTD->nStatus = 0xf;
1111 pTD->pBufferStart = pTD->pBufferEnd - pTD->nBufferSize + 1;
1112 pED->pHeadTD = PHYS_TD(pTD->id) >> 4;
1113
1114 pED->pMetaData->tdList.pushBack(pTD);
1115 break; // Only one TD in a periodic transfer.
1116 }
1117 }
1118
1119 if (!tdBudget && pED->pMetaData->tdList.count()) {
1120 doneHeadDrained = false;
1121 ERROR_NOLOCK("OHCI: ED #" << Dec << pED->pMetaData->id << Hex
1122 << " exceeded the TD scan budget");
1123 }
1124
1125 // If this ED is not queued for deletion, make sure we can use
1126 // it in the next IRQ.
1127 if (!pED->pMetaData->bIgnore)
1128 persistList.pushBack(pED);
1129 }
1130
1131 {
1132 LockGuard<Spinlock> guard(m_ScheduleChangeLock);
1133 if (m_FullSchedule.count()) {
1134 doneHeadDrained = false;
1135 ERROR_NOLOCK("OHCI: exceeded the done-head ED scan budget");
1136 }
1137 }
1138 }
1139
1140 // Restore EDs into the schedule if they were removed and need to
1141 // persist.
1142 if (persistList.count()) {
1143 LockGuard<Spinlock> guard(m_ScheduleChangeLock);
1144 for (List<ED*>::Iterator it = persistList.begin(); it != persistList.end();) {
1145 m_FullSchedule.pushBack(*it);
1146 it = persistList.erase(it);
1147 }
1148 }
1149
1150 // RHSC was acknowledged before its scan so a later port edge cannot be
1151 // erased here.
1152 uint32_t acknowledgeStatus = nStatus & ~OhciInterruptRhStsChange;
1153 if (!sofDrained) {
1154 acknowledgeStatus &= ~OhciInterruptStartOfFrame;
1155 m_pBase->write32(OhciInterruptStartOfFrame, OhciInterruptEnable);
1156 }
1157 if (!doneHeadDrained) {
1158 acknowledgeStatus &= ~OhciInterruptWbDoneHead;
1159 }
1160 if (acknowledgeStatus) {
1161 m_pBase->write32(acknowledgeStatus, OhciInterruptStatus);
1162 (void)m_pBase->read32(OhciInterruptStatus);
1163 }
1164
1165 if (m_TeardownPhase < 2) {
1166 m_pBase->write32(OhciInterruptMIE, OhciInterruptEnable);
1167 }
1168
1169 if (completions.count())
1170 m_CompletionDeliveries.publish(completions);
1171 }
1172
1173 while (completions.count()) {
1174 auto* completion = completions.popFront();
1175 m_CompletionDeliveries.deliver(completion);
1176 }
1177
1178 return IrqDisposition::Handled;
1179}
1180#endif
1181
1182void Ohci::addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid,
1183 uintptr_t pBuffer, size_t nBytes) {
1184 OperationBarrier::Lease submission;
1185 if (!m_SubmissionOperations.tryAcquire(submission))
1186 return;
1187
1188 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1190
1191 constexpr size_t EdCount = OhciDescriptorRegionBytes / sizeof(ED);
1192 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1193 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1194 ED* pED = nullptr;
1195 bool valid = edOffset < EdCount;
1196 if (valid && transactionType == 0) {
1197 valid = m_ControlEDBitmap.test(edOffset);
1198 pED = valid ? &m_pControlEDList[edOffset] : nullptr;
1199 } else if (valid && transactionType == 1) {
1200 valid = m_BulkEDBitmap.test(edOffset);
1201 pED = valid ? &m_pBulkEDList[edOffset] : nullptr;
1202 } else if (valid && transactionType == 2) {
1203 valid = m_PeriodicEDBitmap.test(edOffset);
1204 pED = valid ? &m_pPeriodicEDList[edOffset] : nullptr;
1205 } else
1206 valid = false;
1207
1208 if (pTransaction == static_cast<uintptr_t>(-1) || !valid || !pED || !pED->pMetaData ||
1209 pED->pMetaData->acceptedOperation || pED->pMetaData->bBuildFailed ||
1210 pED->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle) {
1211 ERROR("USB: OHCI: transaction " << pTransaction << " is invalid.");
1212 return;
1213 }
1214
1215 size_t nIndex = m_TDBitmap.getFirstClear();
1216 if (nIndex >= (OhciDescriptorRegionBytes / sizeof(TD))) {
1217 ERROR("USB: OHCI: TD space full");
1218 pED->pMetaData->bBuildFailed = true;
1219 return;
1220 }
1221 m_TDBitmap.set(nIndex);
1222
1223 // Grab the TD pointer we're going to set up now
1224 TD* pTD = &m_pTDList[nIndex];
1225 ByteSet(pTD, 0, sizeof(TD));
1226 pTD->id = nIndex;
1227
1228 // Buffer rounding - allow packets smaller than the buffer we specify
1229 pTD->bBuffRounding = 1;
1230
1231 // PID for the transfer
1232 switch (pid) {
1233 case UsbPidSetup:
1234 pTD->nPid = 0;
1235 break;
1236 case UsbPidOut:
1237 pTD->nPid = 1;
1238 break;
1239 case UsbPidIn:
1240 pTD->nPid = 2;
1241 break;
1242 default:
1243 pTD->nPid = 3;
1244 };
1245
1246 // Active
1247 pTD->nStatus = 0xf;
1248 (void)bToggle;
1249
1250 // Buffer for transfer
1251 if (nBytes) {
1252 const uintptr_t highestVirtual = static_cast<uintptr_t>(-1);
1253 if ((nBytes - 1) > highestVirtual - pBuffer) {
1254 ERROR("OHCI: addTransferToTransaction: buffer range wraps");
1255 pED->pMetaData->bBuildFailed = true;
1256 ByteSet(pTD, 0, sizeof(TD));
1257 m_TDBitmap.clear(nIndex);
1258 return;
1259 }
1260
1261 // A General TD can name the current and final physical 4 KiB pages. It
1262 // cannot describe a third page, regardless of the target VM page size.
1263 const uintptr_t lastBufferByte = pBuffer + nBytes - 1;
1264 const uintptr_t firstHardwarePage = pBuffer & ~uintptr_t(OhciHardwarePageBytes - 1);
1265 const uintptr_t lastHardwarePage = lastBufferByte & ~uintptr_t(OhciHardwarePageBytes - 1);
1266 const size_t hardwarePageCount =
1267 ((lastHardwarePage - firstHardwarePage) / OhciHardwarePageBytes) + 1;
1268 if (hardwarePageCount > OhciBufferPageCount) {
1269 ERROR("OHCI: addTransferToTransaction: buffer spans too many hardware pages");
1270 pED->pMetaData->bBuildFailed = true;
1271 ByteSet(pTD, 0, sizeof(TD));
1272 m_TDBitmap.clear(nIndex);
1273 return;
1274 }
1275
1276 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1277 physical_uintptr_t physicalStart = 0;
1278 physical_uintptr_t physicalEnd = 0;
1279 if (!DriverDma::virtualToPhysical(va, pBuffer, physicalStart) ||
1280 !DriverDma::virtualToPhysical(va, lastBufferByte, physicalEnd) || !physicalStart ||
1281 physicalStart > OhciHighestDmaAddress || physicalEnd > OhciHighestDmaAddress) {
1282 ERROR("OHCI: addTransferToTransaction: buffer range is not DMA-addressable");
1283 pED->pMetaData->bBuildFailed = true;
1284 ByteSet(pTD, 0, sizeof(TD));
1285 m_TDBitmap.clear(nIndex);
1286 return;
1287 }
1288
1289 // Completion and periodic rearming use the controller-updated current
1290 // buffer pointer as a linear byte cursor.
1291 if (!DriverDma::physicalEndpointsAreContiguous(physicalStart, physicalEnd, nBytes,
1292 OhciHighestDmaAddress)) {
1293 ERROR("OHCI: addTransferToTransaction: buffer is not physically contiguous");
1294 pED->pMetaData->bBuildFailed = true;
1295 ByteSet(pTD, 0, sizeof(TD));
1296 m_TDBitmap.clear(nIndex);
1297 return;
1298 }
1299
1300 pTD->pBufferStart = physicalStart;
1301 pTD->pBufferEnd = physicalEnd;
1302 pTD->nBufferSize = nBytes;
1303 }
1304
1305 // This is the last TD so far
1306 pTD->bLast = true;
1307
1308 // Add our TD to the ED's queue.
1309 if (pED->pMetaData->pLastTD) {
1310 pED->pMetaData->pLastTD->pNext = PHYS_TD(nIndex) >> 4;
1311 pED->pMetaData->pLastTD->nNextTDIndex = nIndex;
1312 pED->pMetaData->pLastTD->bLast = false;
1313 } else {
1314 pED->pMetaData->pFirstTD = pTD;
1315 pED->pHeadTD = PHYS_TD(nIndex) >> 4;
1316 }
1317 pED->pMetaData->pLastTD = pTD;
1318
1319 pED->pMetaData->tdList.pushBack(pTD);
1320}
1321
1322uintptr_t Ohci::createTransaction(UsbEndpoint endpointInfo) {
1323 OperationBarrier::Lease submission;
1324 if (!m_SubmissionOperations.tryAcquire(submission))
1325 return static_cast<uintptr_t>(-1);
1326
1327 // Determine what kind of transaction this is.
1328 bool bIsBulk = endpointInfo.nEndpoint > 0;
1329
1330 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1332
1333 ED* pED = nullptr;
1334 size_t nIndex = bIsBulk ? m_BulkEDBitmap.getFirstClear() : m_ControlEDBitmap.getFirstClear();
1335
1336 if (nIndex >= (OhciDescriptorRegionBytes / sizeof(ED))) {
1337 ERROR("USB: OHCI: ED space full");
1338 return static_cast<uintptr_t>(-1);
1339 }
1340
1341 if (bIsBulk) {
1342 m_BulkEDBitmap.set(nIndex);
1343 pED = &m_pBulkEDList[nIndex];
1344 nIndex += OhciDescriptorRegionBytes;
1345 } else {
1346 m_ControlEDBitmap.set(nIndex);
1347 pED = &m_pControlEDList[nIndex];
1348 }
1349
1350 ByteSet(pED, 0, sizeof(ED));
1351
1352 // Device address, endpoint and speed
1353 pED->nAddress = endpointInfo.nAddress;
1354 pED->nEndpoint = endpointInfo.nEndpoint;
1355 pED->bLoSpeed = endpointInfo.speed == LowSpeed;
1356
1357 // Maximum packet size
1358 pED->nMaxPacketSize = endpointInfo.nMaxPacketSize;
1359
1360 // Make sure this ED is ignored until it's properly queued.
1361 pED->bSkip = true;
1362
1363 // Setup the metadata
1364 pED->pMetaData = new ED::MetaData;
1365 pED->pMetaData->endpointInfo = endpointInfo;
1366 pED->pMetaData->id = nIndex;
1367 pED->pMetaData->bIgnore = true; // Don't handle this ED until we're ready.
1368 pED->pMetaData->edType = bIsBulk ? BulkList : ControlList;
1369 pED->pMetaData->bPeriodic = false;
1370 pED->pMetaData->bBuildFailed = false;
1371 pED->pMetaData->pFirstTD = pED->pMetaData->pLastTD = 0;
1372 pED->pMetaData->nTotalBytes = 0;
1373 pED->pMetaData->pPrev = pED->pMetaData->pNext = 0;
1374 pED->pMetaData->bLinked = false;
1375 pED->pMetaData->pCallback = nullptr;
1376 pED->pMetaData->pParam = 0;
1377 pED->pMetaData->acceptedOperation = false;
1378
1379 // Complete
1380 return nIndex;
1381}
1382
1383bool Ohci::doAsync(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
1384 uintptr_t pParam) {
1385 OperationBarrier::Lease submission;
1386 if (!m_SubmissionOperations.tryAcquire(submission))
1387 return false;
1388
1389 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1391
1392 // pTransaction will be 0x0xxx for CONTROL, 0x1xxx for BULK, 0x2xxx for
1393 // PERIODIC.
1394 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1395 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1396 constexpr size_t EdCount = OhciDescriptorRegionBytes / sizeof(ED);
1397
1398 Spinlock* pLock = nullptr;
1399 ED* pED = nullptr;
1400 bool valid = edOffset < EdCount;
1401 if (valid && transactionType == 0) {
1402 valid = m_ControlEDBitmap.test(edOffset);
1403 pED = valid ? &m_pControlEDList[edOffset] : nullptr;
1404 pLock = &m_ControlListChangeLock;
1405 } else if (valid && transactionType == 1) {
1406 valid = m_BulkEDBitmap.test(edOffset);
1407 pED = valid ? &m_pBulkEDList[edOffset] : nullptr;
1408 pLock = &m_BulkListChangeLock;
1409 } else
1410 valid = false;
1411
1412 if (pTransaction == static_cast<uintptr_t>(-1) || !valid) {
1413 ERROR("OHCI: doAsync: didn't get a valid transaction id [" << pTransaction << ", " << edOffset
1414 << "].");
1415 return false;
1416 }
1417
1418 if (!pED->pMetaData) {
1419 ERROR("OHCI: doAsync: transaction metadata is missing");
1420 return false;
1421 }
1422
1423 if (pED->pMetaData->completion.state() != UsbHcd::TransferCompletion::State::Idle) {
1424 ERROR("OHCI: doAsync: transaction is already accepted");
1425 return false;
1426 }
1427
1428 if (pED->pMetaData->bBuildFailed || !pED->pMetaData->pLastTD) {
1429 ERROR("OHCI: doAsync: transaction could not be submitted [" << pTransaction << "].");
1430 retireEDStorage(pED);
1431 return false;
1432 }
1433
1435 retireEDStorage(pED);
1436 return false;
1437 }
1438 pED->pMetaData->acceptedOperation = true;
1439
1440 const bool bControl = transactionType == 0;
1441
1442 // Link the ED while it is still skipped. IRQ completion, cancellation and
1443 // teardown are all excluded by m_IrqProcessingLock until the final unskip.
1444 pLock->acquire();
1445
1446 // Always at the end of the ED queue. Zero means "no next ED" to OHCI.
1447 pED->pNext = 0;
1448
1449 // Handle the case where there is not yet a queue head.
1450 if (bControl) {
1451 if (!m_pControlQueueHead) {
1452 EMIT_IF(UsbVerboseDebug) {
1453 DEBUG_LOG("OHCI: ED is now the control queue head.");
1454 }
1455 m_pControlQueueHead = pED;
1456 }
1457 } else {
1458 if (!m_pBulkQueueHead) {
1459 EMIT_IF(UsbVerboseDebug) {
1460 DEBUG_LOG("OHCI: ED is now the control queue head.");
1461 }
1462 m_pBulkQueueHead = pED;
1463 }
1464 }
1465
1466 // Grab the queue head.
1467 ED* pQueueHead = nullptr;
1468 physical_uintptr_t queueHeadPhys = 0;
1469 if (bControl) {
1470 pQueueHead = m_pControlQueueHead;
1471 queueHeadPhys = vtp_ed(pQueueHead);
1472 } else {
1473 pQueueHead = m_pBulkQueueHead;
1474 queueHeadPhys = vtp_ed(pQueueHead);
1475 }
1476
1477 // Update the head of the relevant list.
1478 if (queueHeadPhys == vtp_ed(pED)) {
1479 if (bControl) {
1480 EMIT_IF(UsbVerboseDebug) {
1481 DEBUG_LOG("OHCI: new control queue head is " << queueHeadPhys << " compared to "
1482 << m_pBase->read32(OhciControlHeadED));
1483 DEBUG_LOG("OHCI: current control queue ED is " << m_pBase->read32(OhciControlCurrentED));
1484 }
1485 m_pBase->write32(queueHeadPhys, OhciControlHeadED);
1486 } else {
1487 EMIT_IF(UsbVerboseDebug) {
1488 DEBUG_LOG("OHCI: new bulk queue head is " << queueHeadPhys);
1489 }
1490 m_pBase->write32(queueHeadPhys, OhciBulkHeadED);
1491 }
1492 }
1493
1494 // Grab the current tail of the list and update it to point to us.
1495 ED* pTail = nullptr;
1496 if (bControl) {
1497 pTail = m_pControlQueueTail;
1498 m_pControlQueueTail = pED;
1499 } else {
1500 pTail = m_pBulkQueueTail;
1501 m_pBulkQueueTail = pED;
1502 }
1503
1504 // Point the old tail to this ED.
1505 if (pTail) {
1506 pTail->pNext = vtp_ed(pED) >> 4;
1507 pTail->pMetaData->pNext = pED;
1508 }
1509
1510 // Fix up the software linked list.
1511 pED->pMetaData->pNext = nullptr;
1512 pED->pMetaData->pPrev = pTail;
1513 pQueueHead->pMetaData->pPrev = nullptr;
1514 pED->pMetaData->bLinked = true;
1515
1516 pLock->release();
1517
1518 {
1519 LockGuard<Spinlock> scheduleGuard(m_ScheduleChangeLock);
1520 m_FullSchedule.pushBack(pED);
1521 }
1522
1523 // Arming immediately before unskip makes the callback obligation and the
1524 // hardware publication one indivisible commit to every competing path.
1525 pED->pMetaData->completion.arm(pCallback, pParam, m_CompletionDeliveries.nextGeneration());
1526 FENCE();
1527 pED->bSkip = pED->pMetaData->bIgnore = false;
1528
1529 // Restart the controller if it was stopped for some reason.
1530 start(pED->pMetaData->edType);
1531
1532 // Tell the controller that the list has valid TD in it now.
1533 // The OHCI will automatically stop processing the ED list if it determines
1534 // no more transfers are pending.
1535 uint32_t status = m_pBase->read32(OhciCommandStatus);
1536 status |= bControl ? OhciCommandControlListFilled : OhciCommandBulkListFilled;
1537 m_pBase->write32(status, OhciCommandStatus);
1538 return true;
1539}
1540
1541void Ohci::cancelAsyncAndDrain(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
1542 uintptr_t pParam) {
1543 OperationBarrier::Lease cancellation;
1544 if (!m_CancellationOperations.tryAcquire(cancellation) || !m_pBase)
1545 return;
1546
1547 bool drainDelivery = false;
1548 UsbHcd::CallbackDeliveryQueue::Key deliveryKey = {0, 0};
1550 ED* pED = nullptr;
1551
1552 {
1553 LockGuard<ControllerLock> controllerGuard(m_Mutex);
1554
1555 // Confirm USBSUSPEND before treating the controller's EDs and TDs as
1556 // software-owned.
1557 uint32_t savedControl = m_pBase->read32(OhciControl);
1558 m_pBase->write32(OhciInterruptMIE, OhciInterruptDisable);
1560 (savedControl & ~OhciControlStateFunctionalMask) | OhciControlStateSuspended,
1561 "OHCI cancellation suspend timed out after 100 ms");
1562
1563 bool restoreController = false;
1564
1565 {
1567
1568 const size_t transactionType = pTransaction / OhciDescriptorRegionBytes;
1569 const uintptr_t edOffset = pTransaction & OhciDescriptorOffsetMask;
1570 constexpr size_t EdCount = OhciDescriptorRegionBytes / sizeof(ED);
1571 bool valid = edOffset < EdCount;
1572 if (valid && transactionType == 0) {
1573 valid = m_ControlEDBitmap.test(edOffset);
1574 pED = valid ? &m_pControlEDList[edOffset] : nullptr;
1575 } else if (valid && transactionType == 1) {
1576 valid = m_BulkEDBitmap.test(edOffset);
1577 pED = valid ? &m_pBulkEDList[edOffset] : nullptr;
1578 } else
1579 valid = false;
1580
1581 Lists completedList = ControlList;
1582 if (valid && pED && pED->pMetaData) {
1584 const auto disposition = pED->pMetaData->completion.claimCancellation(
1585 pCallback, pParam, -TransactionError, claim);
1586 if (disposition == UsbHcd::TransferCompletion::CancellationDisposition::Claimed) {
1587 completedList = pED->pMetaData->edType;
1589 detachED(pED);
1591 completions.pushBack(prepareCompletion(pED, claim));
1592 m_CompletionDeliveries.publish(completions);
1593 } else if (disposition ==
1594 UsbHcd::TransferCompletion::CancellationDisposition::DrainPublished) {
1595 deliveryKey = {pTransaction, claim.generation};
1596 drainDelivery = true;
1597 }
1598
1599 // A natural completion may have stopped its list while waiting
1600 // for SOF. Cancellation supplied that DMA boundary itself.
1601 if (completions.count())
1602 savedControl |= static_cast<uint32_t>(completedList);
1603 }
1604
1605 restoreController = m_TeardownPhase < 2;
1606 if (!restoreController) {
1607 m_pBase->write32(OhciInterruptAll, OhciInterruptDisable);
1608 (void)m_pBase->read32(OhciInterruptEnable);
1609 }
1610 }
1611
1612 if (restoreController) {
1613 // This may restore USBOPERATIONAL or USBRESUME. The state poll is
1614 // outside IRQ serialization so a slow controller cannot hold up
1615 // an interrupt path.
1616 transitionControllerState(savedControl, "OHCI cancellation restore timed out after 100 ms");
1617
1619 m_pBase->write32(OhciInterruptMIE, OhciInterruptEnable);
1620 (void)m_pBase->read32(OhciInterruptEnable);
1621 }
1622 }
1623
1624 while (completions.count()) {
1625 auto* completion = completions.popFront();
1626 m_CompletionDeliveries.deliver(completion);
1627 }
1628 if (drainDelivery)
1629 (void)m_CompletionDeliveries.drain(deliveryKey);
1630}
1631
1633 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
1634 bool producerAlreadyStopped) {
1635 (void)token;
1636 (void)callback;
1637 (void)parameter;
1638 (void)producerAlreadyStopped;
1639 panic("OHCI returned an interrupt-IN handle for an unsupported transfer");
1640 return false;
1641}
1642
1643bool Ohci::addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes,
1644 void (*pCallback)(uintptr_t, ssize_t),
1645 UsbInterruptInHandle& handle, uintptr_t pParam) {
1646 (void)endpointInfo;
1647 (void)pBuffer;
1648 (void)nBytes;
1649 (void)pCallback;
1650 (void)handle;
1651 (void)pParam;
1652 // The legacy OHCI path never built the periodic TD it claimed to publish.
1653 // Failing here retains neither a DMA buffer nor a callback target.
1654 WARNING("USB: OHCI: recurring interrupt-IN transfers are not implemented");
1655 return false;
1656}
1657
1659#if THREADS
1660 if (port >= m_nPorts) {
1661 ERROR("OHCI: invalid suppressed root-port replay " << Dec << port);
1662 return;
1663 }
1664
1666 // Teardown stops publication before its active enumeration worker returns.
1667 if (m_TeardownPhase) {
1668 return;
1669 }
1670 const auto observation = m_PortChanges[port].observe();
1671 const bool accepted = UsbHcd::PortChangeRequest::canAcknowledge(observation.result);
1672 if (accepted) {
1673 m_PortChanges[port].acknowledge(observation.generation);
1674 return;
1675 }
1676
1677 m_TeardownPhase = 1;
1678 {
1679 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
1680 m_RootHubStatusChangeDesired = false;
1682 }
1683 ERROR("OHCI: live suppressed root-port replay could not be published");
1684 assert(false);
1685#else
1686 (void)port;
1687#endif
1688}
1689
1690bool Ohci::portReset(uint8_t nPort, bool bErrorResponse) {
1692
1693 if (nPort >= m_nPorts) {
1694 return false;
1695 }
1696
1697#if THREADS
1699#endif
1700 const size_t portRegister = OhciRhPortStatus + (nPort * 4);
1701
1702 {
1703 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
1704
1705 // PRSC is level-signalled through RHSC. Mask the source while reset is
1706 // in flight so the worker that must clear PRSC cannot be starved by a
1707 // same-core interrupt loop.
1708 m_PortResetActive = true;
1710
1711 // Root-port lower bits are write commands, not an RMW-safe control
1712 // image. Writing only SetPortReset cannot echo unrelated W1C bits.
1713 m_pBase->write32(OhciRhPortStsReset, portRegister);
1714 (void)m_pBase->read32(portRegister);
1715 }
1716
1717 bool resetComplete = false;
1718 constexpr size_t ResetPolls = 200;
1719 for (size_t attempt = 0; attempt < ResetPolls; ++attempt) {
1720 if (m_pBase->read32(portRegister) & OhciRhPortStsResCh) {
1721 resetComplete = true;
1722 break;
1723 }
1724 if (m_TeardownPhase) {
1725 break;
1726 }
1727 Time::delay(5 * Time::Multiplier::Millisecond);
1728 }
1729
1730 {
1731 LockGuard<Spinlock> rootHubGuard(m_RootHubLock);
1732
1733 // The reset worker exclusively owns PRSC while RHSC is masked. A
1734 // completion that arrived at the timeout boundary is still retired.
1735 const uint32_t portStatus = m_pBase->read32(portRegister);
1736 resetComplete = resetComplete || (portStatus & OhciRhPortStsResCh);
1737 if (portStatus & OhciRhPortStsResCh) {
1738 m_pBase->write32(OhciRhPortStsResCh, portRegister);
1739 (void)m_pBase->read32(portRegister);
1740 }
1741
1742 // SetPortEnable is also a command bit; do not echo the status image.
1743 if (resetComplete && !(m_pBase->read32(portRegister) & OhciRhPortStsEnable)) {
1744 m_pBase->write32(OhciRhPortStsEnable, portRegister);
1745 (void)m_pBase->read32(portRegister);
1746 }
1747
1748 m_PortResetActive = false;
1749 if (m_RootHubStatusChangeDesired) {
1750 // Any CSC that arrived during reset remained set while the source
1751 // was masked and becomes deliverable again here.
1753 }
1754 }
1755
1756 if (!resetComplete && !m_TeardownPhase) {
1757 ERROR("OHCI: timed out resetting root port " << nPort);
1758 }
1759 return resetComplete;
1760}
1761
1762uint64_t Ohci::executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5,
1763 uint64_t p6, uint64_t p7, uint64_t p8) {
1764 if (p1 >= m_nPorts) {
1765 return 0;
1766 }
1767 UsbHcd::PortChangeRequest::Completion completion(m_PortChanges[p1], static_cast<size_t>(p8));
1768 if (!completion) {
1769 return 0;
1770 }
1771
1772 // Check for a connected device
1773 if (m_pBase->read32(OhciRhPortStatus + (p1 * 4)) & OhciRhPortStsConnected) {
1774 if (!portReset(p1))
1775 return 0;
1776
1777 // Determine the speed of the attached device
1778 if (m_pBase->read32(OhciRhPortStatus + (p1 * 4)) & OhciRhPortStsLoSpeed) {
1779 DEBUG_LOG("USB: OHCI: Port " << Dec << p1 << Hex
1780 << " has a low-speed device connected to it");
1781 deviceConnected(p1, LowSpeed);
1782 } else {
1783 DEBUG_LOG("USB: OHCI: Port " << Dec << p1 << Hex
1784 << " has a full-speed device connected to it");
1785 deviceConnected(p1, FullSpeed);
1786 }
1787 } else
1789 return 0;
1790}
1791
1792void Ohci::cancelRequest(const Request& request) {
1793 if (request.p1 < m_nPorts) {
1794 m_PortChanges[request.p1].cancel(static_cast<size_t>(request.p8));
1795 }
1796}
1797
1798void Ohci::stop(Lists list) {
1799 if (!m_pHcca)
1800 return;
1801
1802 uint32_t control = m_pBase->read32(OhciControl);
1803 control &= ~static_cast<int>(list);
1804 m_pBase->write32(control, OhciControl);
1805}
1806
1807void Ohci::start(Lists list) {
1808 if (!m_pHcca)
1809 return;
1810
1811 uint32_t control = m_pBase->read32(OhciControl);
1812 control |= static_cast<int>(list);
1813 m_pBase->write32(control, OhciControl);
1814}
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: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
Definition Ohci.h:54
void cancelRequest(const Request &request) override
Definition Ohci.cc:1792
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:1182
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:1383
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:1632
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
Definition Ohci.cc:1322
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:1762
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
Definition Ohci.cc:1690
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:1798
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:1643
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:1658
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:1541
ControllerLock m_Mutex
Global lock.
Definition Ohci.h:369
void start(Lists list)
Starts processing of the given list.
Definition Ohci.cc:1807
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)
void writeConfigSpace(Device *pDev, uint8_t offset, uint32_t data)
static PhysicalMemoryManager & instance()
static ProcessorInformation & information()
virtual void destroy()
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
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:388
void deviceDisconnected(uint8_t nPort)
Called when a device is disconnected from a port on the hub.
Definition UsbHub.cc:550
MUST_USE_RESULT bool deferConnectionChangeIfSuppressed(size_t port)
Definition UsbHub.cc:303
void disconnectAllDevices()
Definition UsbHub.cc:217
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ 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 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