20#include "modules/system/usb/UsbHub.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/ServiceManager.h"
24#include "pedigree/kernel/panic.h"
25#include "pedigree/kernel/process/Scheduler.h"
26#include "pedigree/kernel/process/TerminationDeferral.h"
28#include "modules/system/usb/Usb.h"
29#include "modules/system/usb/UsbDevice.h"
30#include "modules/system/usb/UsbPnP.h"
31#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
32#include "pedigree/kernel/process/OperationBarrier.h"
33#include "pedigree/kernel/process/Thread.h"
34#include "pedigree/kernel/processor/Processor.h"
35#include "pedigree/kernel/time/Time.h"
37#include "pedigree/kernel/utilities/ExtensibleBitmap.h"
38#include "pedigree/kernel/utilities/List.h"
39#include "pedigree/kernel/utilities/Vector.h"
40#include "pedigree/kernel/utilities/assert.h"
41#include "pedigree/kernel/utilities/new"
43#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
46void (*g_HostedSyncTimeoutHook)() =
nullptr;
47UsbHub::ConnectionChangePendingHook g_ConnectionChangePendingHook =
nullptr;
48UsbHub::ConnectionChangeReplayWaitHook g_ConnectionChangeReplayWaitHook =
nullptr;
52UsbInterruptInHandle::UsbInterruptInHandle()
55 m_Token{static_cast<uintptr_t>(-1), 0},
58 m_CancellationStarted(false) {}
61 : m_Owner(static_cast<
UsbHub*>(other.m_Owner)),
63 m_Token(other.m_Token),
64 m_Callback(other.m_Callback),
65 m_Parameter(other.m_Parameter),
66 m_CancellationStarted(other.m_CancellationStarted) {
67 assert(!other.m_Resetting);
68 other.m_Owner =
nullptr;
69 other.m_Token = {
static_cast<uintptr_t
>(-1), 0};
70 other.m_Callback =
nullptr;
71 other.m_Parameter = 0;
72 other.m_CancellationStarted =
false;
75UsbInterruptInHandle::~UsbInterruptInHandle() {
77 panic(
"USB interrupt-IN handle destroyed from its running callback");
83 panic(
"USB interrupt-IN handle move attempted from its running callback");
84 assert(!other.m_Resetting);
85 m_Owner =
static_cast<UsbHub*
>(other.m_Owner);
86 m_Token = other.m_Token;
87 m_Callback = other.m_Callback;
88 m_Parameter = other.m_Parameter;
89 m_CancellationStarted = other.m_CancellationStarted;
90 other.m_Owner =
nullptr;
91 other.m_Token = {
static_cast<uintptr_t
>(-1), 0};
92 other.m_Callback =
nullptr;
93 other.m_Parameter = 0;
94 other.m_CancellationStarted =
false;
100 while (!m_Resetting.compareAndSwap(
false,
true)) {
102 if (owner && owner->inInterruptCallbackContext())
113 if (!owner->cancelInterruptInAndDrain(m_Token, m_Callback, m_Parameter, m_CancellationStarted)) {
114 m_CancellationStarted =
true;
120 m_Token = {
static_cast<uintptr_t
>(-1), 0};
121 m_Callback =
nullptr;
123 m_CancellationStarted =
false;
129 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter) {
130 assert(!
static_cast<UsbHub*
>(m_Owner));
131 assert(!m_Resetting);
133 m_Callback = callback;
134 m_Parameter = parameter;
135 m_CancellationStarted =
false;
140UsbHub::ConnectionChangeSuppression::ConnectionChangeSuppression() : m_Hub(nullptr), m_Port(0) {}
142UsbHub::ConnectionChangeSuppression::ConnectionChangeSuppression(
UsbHub* hub,
size_t port)
143 : m_Hub(hub), m_Port(port) {}
145UsbHub::ConnectionChangeSuppression::ConnectionChangeSuppression(
146 ConnectionChangeSuppression&& other)
147 : m_Hub(other.m_Hub), m_Port(other.m_Port) {
148 other.m_Hub =
nullptr;
152UsbHub::ConnectionChangeSuppression::~ConnectionChangeSuppression() {
157 ConnectionChangeSuppression&& other) {
158 if (
this != &other) {
161 m_Port = other.m_Port;
162 other.m_Hub =
nullptr;
168void UsbHub::ConnectionChangeSuppression::reset() {
171 const size_t port = m_Port;
174 hub->releaseConnectionChangeSuppression(port);
180 m_UsedAddresses.set(0);
181 m_AddressReferences[0] = 1;
185 :
Device(p), m_RootHub(this), m_IsRootHub(true), m_RootPort(0xff), m_RootPortGeneration(0) {
186 m_UsedAddresses.set(0);
187 m_AddressReferences[0] = 1;
192UsbHub::StartupActivity::StartupActivity(
UsbHub* hub)
193 : m_Root(hub ? hub->m_RootHub : nullptr),
194 m_Admitted(!m_Root || m_Root->beginStartupActivity()) {}
196UsbHub::StartupActivity::~StartupActivity() {
197 if (m_Root && m_Admitted)
198 m_Root->endStartupActivity(m_Outcome);
209 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter) {
210 if (handle || !callback || token.transaction ==
static_cast<uintptr_t
>(-1) || !token.generation)
213 handle.adopt(
this, token, callback, parameter);
226 UsbDevice* device = container->getUsbDevice();
232 container->closeProbeAdmission();
236 container->waitForProbes();
252 ERROR(
"USB: downstream hub has no root-controller association");
265 if (port >= ConnectionChangePortCount) {
268 "USB: root-port connection-change suppression was requested for "
276 const size_t observed = state;
277 const size_t count = observed & ConnectionChangeCountMask;
278 if (!count && (observed & ConnectionChangePending)) {
279#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
280 if (g_ConnectionChangeReplayWaitHook) {
281 g_ConnectionChangeReplayWaitHook(
this, port);
287 if (count == ConnectionChangeCountMask) {
290 "USB: root-port connection-change suppression count "
291 "overflowed on port "
296 if (state.compareAndSwap(observed, observed + 1)) {
304 if (port >= ConnectionChangePortCount) {
310 const size_t observed = state;
311 if (!(observed & ConnectionChangeCountMask)) {
315#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
316 if (g_ConnectionChangePendingHook) {
317 g_ConnectionChangePendingHook(
this, port, observed);
325 if (state.compareAndSwap(observed, observed | ConnectionChangePending)) {
331void UsbHub::releaseConnectionChangeSuppression(
size_t port) {
332 if (port >= ConnectionChangePortCount) {
333 ERROR(
"USB: invalid root-port connection-change suppression release");
340 const size_t observed = state;
341 const size_t count = observed & ConnectionChangeCountMask;
344 "USB: unbalanced root-port connection-change suppression "
350 const bool finalRelease = count == 1;
354 const size_t replacement =
355 finalRelease ? ((observed & ConnectionChangePending) ? ConnectionChangePending : 0)
357 if (state.compareAndSwap(observed, replacement)) {
358 replay = finalRelease && (observed & ConnectionChangePending);
365 const bool published = state.compareAndSwap(ConnectionChangePending, 0);
373 "USB: root-port change replay has no controller implementation for "
378#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
379void UsbHub::setConnectionChangePendingHookForTest(ConnectionChangePendingHook hook) {
380 g_ConnectionChangePendingHook = hook;
383void UsbHub::setConnectionChangeReplayWaitHookForTest(ConnectionChangeReplayWaitHook hook) {
384 g_ConnectionChangeReplayWaitHook = hook;
392 const bool connected = deviceConnectedAdmitted(nPort, speed);
398bool UsbHub::deviceConnectedAdmitted(uint8_t nPort, UsbSpeed speed) {
399 NOTICE(
"USB: Adding device on port " <<
Dec << nPort <<
Hex);
403 ERROR(
"USB: cannot enumerate a hub without a root controller");
409 ConnectionChangeSuppression changeSuppression;
414 struct PendingProbe {
415 explicit PendingProbe(
UsbDeviceContainer* container) : container(container), probe() {}
428 bool retiredAddresses[128] = {};
429 bool pinnedSubtreeAddresses[128] = {};
430 retirePortContainersLocked(nPort, retiredAddresses, pinnedSubtreeAddresses);
437 releaseRetiredAddressesLocked(nPort, retiredAddresses);
441 NOTICE(
"USB: Port reset failed before enumeration (port " << nPort <<
")");
444 pRootHub->releaseAddressesLocked(pinnedSubtreeAddresses);
446 WARNING(
"USB: couldn't determine reset port speed (port " << nPort <<
")");
451 uint8_t nAddress = 0;
457 if (nAddress > 127) {
458 ERROR(
"USB: HUB: Out of addresses!");
465 NOTICE(
"USB: Allocated device on port " <<
Dec << nPort <<
Hex <<
" address " << nAddress);
467 pDevice =
new UsbDevice(
this, nPort, speed);
470 if (pDevice->
getUsbState() != UsbDevice::Configured) {
472 "USB: Device initialisation ended up not giving a configured "
474 << nRetry <<
" of 2].");
479 NOTICE(
"USB: Performing a port reset on port " << nPort);
484 NOTICE(
"USB: Port reset failed (port " << nPort <<
")");
487 pRootHub->releaseAddressLocked(nAddress);
489 NOTICE(
"USB: Device on port " <<
Dec << nPort <<
Hex <<
" accepted address " << nAddress);
497 NOTICE(
"Device initialisation couldn't configure the device.");
503 for (
size_t i = 0; i < interfaceList.
count(); i++) {
505 if (pInterface->nAlternateSetting)
512 auto* pending =
new PendingProbe(pContainer);
513 if (!pContainer->tryAcquireProbe(pending->probe))
514 panic(
"USB: new device container rejected its initial probe");
522 NOTICE(
"USB: Device (address " << nAddress <<
"): " << pDescriptor->sVendor <<
" "
523 << pDescriptor->sProduct <<
", class " <<
Dec
524 << pInterface->nClass <<
":" << pInterface->nSubclass <<
":"
525 << pInterface->nProtocol <<
Hex);
528 if (!pendingProbes.
count()) {
531 WARNING(
"USB: configured device exposed no default interface");
533 pRootHub->releaseAddressLocked(nAddress);
540 changeSuppression.reset();
542 while (pendingProbes.
count()) {
543 PendingProbe* pending = pendingProbes.
popFront();
552 deviceDisconnectedLocked(nPort);
555void UsbHub::deviceDisconnectedLocked(uint8_t nPort) {
556 bool retiredAddresses[128] = {};
557 bool pinnedSubtreeAddresses[128] = {};
558 retirePortContainersLocked(nPort, retiredAddresses,
563 releaseRetiredAddressesLocked(nPort, retiredAddresses);
568void UsbHub::retirePortContainersLocked(uint8_t nPort,
bool* retiredAddresses,
569 bool* pinnedSubtreeAddresses) {
581 UsbDevice* pDevice = container->getUsbDevice();
582 if (!pDevice || pDevice->
getPort() != nPort) {
587 const uint8_t address = pDevice->
getAddress();
588 if (address && address < 128)
589 retiredAddresses[address] =
true;
591 container->closeProbeAdmission();
594 retiredContainers.
pushBack(container);
599 it != retiredContainers.
end(); ++it) {
600 drainSubtreeProbeAdmissions(*it);
603 if (pinnedSubtreeAddresses) {
608 it != retiredContainers.
end(); ++it) {
609 collectUsbAddresses(*it, pinnedSubtreeAddresses);
611 addressOwner->retainAddressesLocked(pinnedSubtreeAddresses);
614 while (retiredContainers.
count()) {
623 panic(
"USB: couldn't withdraw filesystems before device retirement");
626 prepareSubtreeForDisconnection(container);
638 UsbDevice* usbDevice = container->getUsbDevice();
640 const uint8_t address = usbDevice->
getAddress();
641 if (address && address < 128)
646 for (
size_t i = 0; i < device->getNumChildren(); ++i)
647 collectUsbAddresses(device->getChild(i),
addresses);
654 for (
size_t i = 0; i < device->getNumChildren(); ++i) {
655 Device* child = device->getChild(i);
659 collectImmediateUsbContainers(child, containers);
671 container->closeProbeAdmission();
672 container->waitForProbes();
673 UsbDevice* device = container->getUsbDevice();
675 device->quiesceForRetirement();
681 collectImmediateUsbContainers(container, children);
684 drainSubtreeProbeAdmissions(*it);
688 UsbDevice* device = container->getUsbDevice();
690 device->prepareForDisconnection();
695 collectImmediateUsbContainers(container, children);
698 prepareSubtreeForDisconnection(*it);
702void UsbHub::retainAddressLocked(
size_t address) {
703 assert(address && address < 128);
709void UsbHub::releaseAddressLocked(
size_t address) {
710 assert(address && address < 128);
719void UsbHub::retainAddressesLocked(
const bool*
addresses) {
720 for (
size_t address = 1; address < 128; ++address) {
722 retainAddressLocked(address);
726void UsbHub::releaseAddressesLocked(
const bool*
addresses) {
727 for (
size_t address = 1; address < 128; ++address) {
729 releaseAddressLocked(address);
733void UsbHub::releaseRetiredAddressesLocked(uint8_t nPort,
const bool* retiredAddresses) {
734 bool retiredAny =
false;
735 for (
size_t address = 1; address < 128; ++address) {
736 if (!retiredAddresses[address])
740 m_RootHub->releaseAddressLocked(address);
741 NOTICE(
"USB: Retired device on port " <<
Dec << nPort <<
" address " << address <<
Hex);
745 ERROR(
"USB: cannot release an address without a root controller");
752 pSyncParam->nResult = nResult;
753 pSyncParam->semaphore.
release();
754 pSyncParam->releaseOwner();
757UsbHub::SyncParam::~SyncParam() {
758#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
759 g_HostedSyncParamDestructions += 1;
763void UsbHub::SyncParam::releaseOwner() {
764 if ((owners -= 1) == 0) {
778 const uintptr_t callbackParam =
reinterpret_cast<uintptr_t
>(pSyncParam);
780 pSyncParam->releaseOwner();
781 pSyncParam->releaseOwner();
782 return -TransactionError;
787 Semaphore::SemaphoreError waitError = Semaphore::NoError;
788 const bool completed =
789 pSyncParam->semaphore.
acquireWithError(1, timeout / 1000, (timeout % 1000) * 1000, waitError);
790 const bool bTimeout = !completed && waitError == Semaphore::TimedOut;
792#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
793 if (bTimeout && g_HostedSyncTimeoutHook) {
794 g_HostedSyncTimeoutHook();
804 (void)callbackDrained;
807 const ssize_t result = completed ? pSyncParam->nResult : -TransactionError;
809 WARNING(
"USB: a transaction timed out.");
811 pSyncParam->releaseOwner();
815#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
817struct HostedSyncCallback {
826 bufferOwned(false) {}
828 void (*callback)(uintptr_t, ssize_t);
838int hostedSyncCallbackThread(
void* parameter) {
839 HostedSyncCallback* context =
reinterpret_cast<HostedSyncCallback*
>(parameter);
840 const bool started = context->start.acquireForCompletion();
842 if (context->completionState.compareAndSwap(1, 2)) {
843 context->bufferOwned =
false;
844 context->callbackCount += 1;
845 context->callback(context->parameter, 0x55);
847 context->finished.release();
851class HostedSyncHub :
public UsbHub {
853 explicit HostedSyncHub(
bool accept =
true) :
UsbHub(), m_Callback(), m_Accept(accept) {}
855 ~HostedSyncHub()
override {
856 if (m_Callback.thread) {
857 m_Callback.start.release();
858 m_Callback.thread->joinForCompletion();
859 m_Callback.thread =
nullptr;
863 void addTransferToTransaction(uintptr_t,
bool, UsbPid, uintptr_t,
size_t)
override {}
865 uintptr_t createTransaction(
UsbEndpoint)
override {
869 bool doAsync(uintptr_t,
void (*callback)(uintptr_t, ssize_t), uintptr_t parameter)
override {
873 m_Callback.callback = callback;
874 m_Callback.parameter = parameter;
875 m_Callback.completionState = 1;
876 m_Callback.bufferOwned =
true;
878 hostedSyncCallbackThread, &m_Callback,
nullptr,
false,
true);
879 m_Callback.thread->setName(
"hosted USB timeout callback");
883 void cancelAsyncAndDrain(uintptr_t,
void (*callback)(uintptr_t, ssize_t),
884 uintptr_t parameter)
override {
885 if (m_Callback.callback == callback && m_Callback.parameter == parameter &&
886 m_Callback.completionState.compareAndSwap(1, 2)) {
889 m_Callback.bufferOwned =
false;
890 m_Callback.callbackCount += 1;
891 callback(parameter, -TransactionError);
894 if (m_Callback.thread) {
895 m_Callback.start.release();
896 const bool finished = m_Callback.finished.acquireForCompletion();
898 m_Callback.thread->joinForCompletion();
899 m_Callback.thread =
nullptr;
903 bool addInterruptInHandler(
UsbEndpoint, uintptr_t, uint16_t,
void (*)(uintptr_t, ssize_t),
908 bool portReset(uint8_t,
bool)
override {
912 void completeInsideTimeoutHandoff() {
913 m_Callback.start.release();
914 const bool finished = m_Callback.finished.acquireForCompletion();
916 m_Callback.thread->joinForCompletion();
917 m_Callback.thread =
nullptr;
920 bool completedExactlyOnce()
const {
921 return static_cast<size_t>(m_Callback.callbackCount) == 1 &&
922 !
static_cast<bool>(m_Callback.bufferOwned);
927 uintptr_t,
bool)
override {
928 panic(
"hosted synchronous USB hub unexpectedly owned an interrupt subscription");
932 HostedSyncCallback m_Callback;
936HostedSyncHub* g_pHostedSyncHub =
nullptr;
938void completeHostedSyncTimeout() {
939 g_pHostedSyncHub->completeInsideTimeoutHandoff();
943bool UsbHub::runHostedSyncOwnershipRegression() {
944 const size_t destructionsBefore = g_HostedSyncParamDestructions;
945 bool rejectionPassed =
false;
947 HostedSyncHub hub(
false);
948 rejectionPassed = hub.doSync(1, 1) == -TransactionError;
950 if (rejectionPassed) {
951 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-sync-rejected-no-callback");
954 "HOSTED-WAIT-TEST: FAIL usb-sync-rejected-no-callback: rejected "
955 "submission retained a callback obligation");
958 bool cancelPassed =
false;
961 const ssize_t result = hub.doSync(1, 1);
962 cancelPassed = result == -TransactionError && hub.completedExactlyOnce();
965 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-sync-timeout-cancel-drain");
968 "HOSTED-WAIT-TEST: FAIL usb-sync-timeout-cancel-drain: doSync "
969 "returned before cancellation released the fake DMA buffer");
972 bool handoffPassed =
false;
975 g_pHostedSyncHub = &hub;
976 g_HostedSyncTimeoutHook = completeHostedSyncTimeout;
977 const ssize_t result = hub.doSync(1, 1);
978 g_HostedSyncTimeoutHook =
nullptr;
979 g_pHostedSyncHub =
nullptr;
980 handoffPassed = result == -TransactionError && hub.completedExactlyOnce();
983 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-sync-timeout-completion-handoff");
986 "HOSTED-WAIT-TEST: FAIL usb-sync-timeout-completion-handoff: "
987 "completion/cancellation race delivered more than once");
990 return rejectionPassed && cancelPassed && handoffPassed &&
991 g_HostedSyncParamDestructions == (destructionsBefore + 3);
994EXPORTED_PUBLIC
bool runHostedUsbSyncOwnershipRegression() {
995 return UsbHub::runHostedSyncOwnershipRegression();
999#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
1001constexpr bool PinInterruptTestThreads = HOSTED;
1003void* interruptRegressionRunner() {
1005 Thread* thread = information.getCurrentThread();
1006 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
1009class InterruptOwnershipHub :
public UsbHub {
1011 InterruptOwnershipHub()
1014 m_Callback(nullptr),
1016 m_CallbackRunner(nullptr),
1019 ~InterruptOwnershipHub()
override {
1020 if (m_Callbacks.isOpen())
1021 m_Callbacks.closeAndWait();
1024 void addTransferToTransaction(uintptr_t,
bool, UsbPid, uintptr_t,
size_t)
override {}
1026 uintptr_t createTransaction(
UsbEndpoint)
override {
1027 return static_cast<uintptr_t
>(-1);
1030 bool doAsync(uintptr_t,
void (*)(uintptr_t, ssize_t), uintptr_t)
override {
1034 void cancelAsyncAndDrain(uintptr_t,
void (*)(uintptr_t, ssize_t), uintptr_t)
override {
1035 panic(
"interrupt ownership regression unexpectedly cancelled an async transfer");
1038 bool addInterruptInHandler(
UsbEndpoint, uintptr_t, uint16_t,
void (*callback)(uintptr_t, ssize_t),
1040 if (m_Callback || !callback || handle || !m_Callbacks.isOpen())
1044 if (!publishInterruptInHandle(handle, token, callback, parameter))
1047 m_Callback = callback;
1048 m_Parameter = parameter;
1052 bool portReset(uint8_t,
bool)
override {
1058 if (!m_Callback || !m_Callbacks.tryAcquire(callbackLease))
1061 m_CallbackRunner = interruptRegressionRunner();
1062 m_Callback(m_Parameter, 1);
1063 m_CallbackRunner =
nullptr;
1067 size_t cancelCalls()
const {
1068 return m_CancelCalls;
1072 bool inInterruptCallbackContext()
const override {
1073 return m_CallbackRunner == interruptRegressionRunner();
1077 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
1078 bool producerAlreadyStopped)
override {
1079 if (token.transaction != 0x494e5452 || token.generation != 1 || callback != m_Callback ||
1080 parameter != m_Parameter) {
1081 panic(
"interrupt ownership regression received a stale cancellation token");
1084 if (!producerAlreadyStopped) {
1086 m_Callbacks.close();
1089 if (m_CallbackRunner == interruptRegressionRunner())
1097 void (*m_Callback)(uintptr_t, ssize_t);
1098 uintptr_t m_Parameter;
1100 size_t m_CancelCalls;
1103class ProbeLeafUsbDevice :
public UsbDevice {
1105 ProbeLeafUsbDevice(
UsbHub* hub, uint8_t port, uint8_t address) :
UsbDevice(hub, port, FullSpeed) {
1106 m_nAddress = address;
1112 ProbeSubtreeUsbDevice(
UsbHub* hub, uint8_t port, uint8_t address)
1114 m_nAddress = address;
1119 bool hasSubtree()
const override {
1123 Device* getDevice()
override {
1128struct DescendantProbePublication {
1130 : container(container), probe(), entered(0), publish(0), published(0) {}
1139int publishDescendantProbe(
void* parameter) {
1140 auto* context =
reinterpret_cast<DescendantProbePublication*
>(parameter);
1141 context->entered.release();
1142 const bool released = context->publish.acquireForCompletion();
1145 UsbDevice*
generic = context->container->getUsbDevice();
1146 auto* replacement =
new ProbeSubtreeUsbDevice(generic);
1150 grandchild->setParent(replacement);
1151 replacement->addChild(grandchild);
1153 if (context->container->replaceUsbDevice(replacement))
1154 context->published += 1;
1159struct ProbeSubtreeRetirement {
1160 ProbeSubtreeRetirement(
UsbHub* hub, uint8_t port)
1161 : hub(hub), port(port), started(0), finished(0), retired{}, pinned{} {}
1171struct ExternalInterruptCancellation {
1182 callbackResetRejected(0),
1183 callbackProcessor(static_cast<size_t>(-1)),
1184 resetProcessor(static_cast<size_t>(-1)) {}
1186 InterruptOwnershipHub* hub;
1200void blockingInterruptCallback(uintptr_t parameter, ssize_t) {
1201 auto* context =
reinterpret_cast<ExternalInterruptCancellation*
>(parameter);
1203 context->callbackCalls += 1;
1204 context->callbackEntered.release();
1205 const bool released = context->releaseCallback.acquireForCompletion();
1207 if (!context->handle->reset())
1208 context->callbackResetRejected += 1;
1211int fireInterruptCallback(
void* parameter) {
1212 auto* context =
reinterpret_cast<ExternalInterruptCancellation*
>(parameter);
1213 if (context->hub->fire())
1214 context->fireReturned += 1;
1218int resetInterruptHandle(
void* parameter) {
1219 auto* context =
reinterpret_cast<ExternalInterruptCancellation*
>(parameter);
1221 context->resetStarted += 1;
1222 if (context->handle->reset())
1223 context->resetSucceeded += 1;
1224 context->resetReturned += 1;
1228struct SelfInterruptCancellation {
1230 : handle(handle), callbacks(0), resetRejected(0), ownershipRetained(0) {}
1238void selfCancellingInterruptCallback(uintptr_t parameter, ssize_t) {
1239 auto* context =
reinterpret_cast<SelfInterruptCancellation*
>(parameter);
1240 context->callbacks += 1;
1241 if (!context->handle->reset())
1242 context->resetRejected += 1;
1243 if (*context->handle)
1244 context->ownershipRetained += 1;
1247bool waitForInterruptValue(
Atomic<size_t>& value,
size_t expected) {
1248 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1249 while (value != expected && Time::getTicks() < deadline)
1251 return value == expected;
1254bool waitForInterruptDrain(
Thread* thread) {
1255 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1256 while (thread->
getStatus() != Thread::Sleeping && Time::getTicks() < deadline)
1258 return thread->
getStatus() == Thread::Sleeping;
1262int UsbHub::retireProbeSubtreeForTest(
void* parameter) {
1263 auto* context =
reinterpret_cast<ProbeSubtreeRetirement*
>(parameter);
1264 context->started += 1;
1265 context->hub->retirePortContainersLocked(context->port, context->retired, context->pinned);
1266 context->finished += 1;
1270bool UsbHub::runInterruptOwnershipRegression() {
1271 bool subtreeAddressPinPassed =
false;
1273 InterruptOwnershipHub hub;
1274 constexpr size_t ParentAddress = 5;
1275 constexpr size_t DescendantAddress = 6;
1276 hub.m_UsedAddresses.set(ParentAddress);
1277 hub.m_UsedAddresses.set(DescendantAddress);
1278 hub.m_AddressReferences[ParentAddress] = 1;
1279 hub.m_AddressReferences[DescendantAddress] = 1;
1281 bool subtreeAddresses[128] = {};
1282 subtreeAddresses[ParentAddress] =
true;
1283 subtreeAddresses[DescendantAddress] =
true;
1284 hub.retainAddressesLocked(subtreeAddresses);
1289 hub.releaseAddressLocked(ParentAddress);
1290 hub.releaseAddressLocked(DescendantAddress);
1291 const bool heldThroughDrain =
1292 hub.m_UsedAddresses.test(ParentAddress) && hub.m_UsedAddresses.test(DescendantAddress);
1293 hub.releaseAddressesLocked(subtreeAddresses);
1294 subtreeAddressPinPassed = heldThroughDrain && !hub.m_UsedAddresses.test(ParentAddress) &&
1295 !hub.m_UsedAddresses.test(DescendantAddress);
1298 bool driverUnbindPinPassed =
false;
1300 InterruptOwnershipHub hub;
1301 constexpr size_t DescendantAddress = 7;
1302 hub.m_UsedAddresses.set(DescendantAddress);
1303 hub.m_AddressReferences[DescendantAddress] = 1;
1305 bool descendantAddresses[128] = {};
1306 descendantAddresses[DescendantAddress] =
true;
1307 hub.retainAddressesLocked(descendantAddresses);
1308 hub.releaseAddressLocked(DescendantAddress);
1312 driverUnbindPinPassed = hub.m_UsedAddresses.test(DescendantAddress) &&
1313 hub.m_AddressReferences[DescendantAddress] == 1;
1316 bool subtreeProbeFixedPointPassed =
false;
1318 InterruptOwnershipHub hub;
1319 constexpr size_t ParentAddress = 5;
1320 constexpr size_t DescendantAddress = 6;
1321 constexpr size_t GrandchildAddress = 7;
1322 constexpr size_t ReplacedGrandchildAddress = 8;
1323 for (
size_t address = ParentAddress; address <= ReplacedGrandchildAddress; ++address) {
1324 hub.m_UsedAddresses.set(address);
1325 hub.m_AddressReferences[address] = 1;
1328 auto* parentDevice =
new ProbeSubtreeUsbDevice(&hub, 1, ParentAddress);
1330 auto* descendantDevice =
new ProbeSubtreeUsbDevice(&hub, 2, DescendantAddress);
1332 auto* replacedGrandchild =
1336 replacedGrandchild->setParent(descendantDevice);
1337 descendantDevice->addChild(replacedGrandchild);
1338 descendantContainer->setParent(parentDevice);
1339 parentDevice->addChild(descendantContainer);
1340 parentContainer->setParent(&hub);
1341 hub.addChild(parentContainer);
1344 DescendantProbePublication publication(descendantContainer);
1345 const bool probeAdmitted = descendantContainer->tryAcquireProbe(publication.probe);
1347 Thread* publisher =
nullptr;
1348 Thread* retiree =
nullptr;
1349 bool publisherEntered =
false;
1350 bool retirementBlocked =
false;
1351 ProbeSubtreeRetirement retirement(&hub, 1);
1352 if (probeAdmitted) {
1353 publisher =
new Thread(process, publishDescendantProbe, &publication,
nullptr,
false,
1354 PinInterruptTestThreads);
1355 publisher->setName(
"USB descendant probe publisher");
1356 publisherEntered = publication.entered.acquireForCompletion();
1358 if (publisherEntered) {
1359 retiree =
new Thread(process, retireProbeSubtreeForTest, &retirement,
nullptr,
false,
1360 PinInterruptTestThreads);
1361 retiree->setName(
"USB subtree retirement");
1362 const bool retirementStarted = waitForInterruptValue(retirement.started, 1);
1364 retirementStarted && waitForInterruptDrain(retiree) && !retirement.finished;
1367 publication.publish.release();
1370 subtreeProbeFixedPointPassed =
1371 probeAdmitted && publisherEntered && retirementBlocked && publisherJoined &&
1372 retireeJoined && publication.published ==
static_cast<size_t>(1) &&
1373 retirement.finished ==
static_cast<size_t>(1) && retirement.retired[ParentAddress] &&
1374 retirement.pinned[ParentAddress] && retirement.pinned[DescendantAddress] &&
1375 retirement.pinned[GrandchildAddress] && !retirement.pinned[ReplacedGrandchildAddress] &&
1376 hub.m_AddressReferences[GrandchildAddress] == 2;
1378 hub.releaseAddressesLocked(retirement.pinned);
1379 for (
size_t address = ParentAddress; address <= ReplacedGrandchildAddress; ++address)
1380 hub.releaseAddressLocked(address);
1383 bool externalPassed =
false;
1384 size_t callbackProcessor =
static_cast<size_t>(-1);
1385 size_t resetProcessor =
static_cast<size_t>(-1);
1386#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1387 constexpr size_t MaxExternalAttempts = 16;
1389 constexpr size_t MaxExternalAttempts = 1;
1391 size_t externalAttempts = 0;
1392 while (externalAttempts < MaxExternalAttempts && !externalPassed) {
1394 InterruptOwnershipHub hub;
1396 ExternalInterruptCancellation context(&hub, &handle);
1398 const bool submitted = hub.addInterruptInHandler(endpoint, 0x1000, 8, blockingInterruptCallback,
1399 handle,
reinterpret_cast<uintptr_t
>(&context));
1402 Thread* delivery =
nullptr;
1403 Thread* cancellation =
nullptr;
1404 bool callbackEntered =
false;
1405 bool resetStarted =
false;
1406 bool resetBlocked =
false;
1408 delivery =
new Thread(process, fireInterruptCallback, &context,
nullptr,
false,
1409 PinInterruptTestThreads);
1410 delivery->setName(
"USB interrupt ownership callback");
1411 callbackEntered = context.callbackEntered.acquireForCompletion();
1413 if (callbackEntered) {
1414 cancellation =
new Thread(process, resetInterruptHandle, &context,
nullptr,
false,
1415 PinInterruptTestThreads);
1416 cancellation->setName(
"USB interrupt ownership cancellation");
1417 resetStarted = waitForInterruptValue(context.resetStarted, 1);
1418 resetBlocked = resetStarted && waitForInterruptDrain(cancellation) && !context.resetReturned;
1421 context.releaseCallback.release();
1423 const bool cancellationJoined = !cancellation || cancellation->
joinForCompletion();
1424 bool cleanupSucceeded =
true;
1426 cleanupSucceeded = handle.
reset();
1428 callbackProcessor = context.callbackProcessor;
1429 resetProcessor = context.resetProcessor;
1430 const bool semanticsPassed = submitted && callbackEntered && resetBlocked && deliveryJoined &&
1431 cancellationJoined && cleanupSucceeded &&
1432 context.callbackCalls ==
static_cast<size_t>(1) &&
1433 context.fireReturned ==
static_cast<size_t>(1) &&
1434 context.resetReturned ==
static_cast<size_t>(1) &&
1435 context.resetSucceeded ==
static_cast<size_t>(1) && !handle &&
1436 context.callbackResetRejected ==
static_cast<size_t>(1) &&
1437 hub.cancelCalls() == 1 && !hub.fire();
1438 if (!semanticsPassed)
1440#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1441 externalPassed = callbackProcessor != resetProcessor;
1443 externalPassed =
true;
1447 bool selfPassed =
false;
1449 InterruptOwnershipHub hub;
1451 SelfInterruptCancellation context(&handle);
1453 const bool submitted =
1454 hub.addInterruptInHandler(endpoint, 0x1000, 8, selfCancellingInterruptCallback, handle,
1455 reinterpret_cast<uintptr_t
>(&context));
1456 const bool fired = submitted && hub.fire();
1457 const bool retained =
static_cast<bool>(handle);
1458 const bool retried = retained && handle.
reset();
1459 selfPassed = submitted && fired && context.callbacks ==
static_cast<size_t>(1) &&
1460 context.resetRejected ==
static_cast<size_t>(1) &&
1461 context.ownershipRetained ==
static_cast<size_t>(1) && retained && retried &&
1462 !handle && hub.cancelCalls() == 1 && !hub.fire();
1465#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1466 NOTICE(
"QEMU-CONCURRENCY-TEST: usb-interrupt-cancel cpus="
1467 <<
Dec << callbackProcessor <<
"/" << resetProcessor <<
" attempts=" << externalAttempts);
1469 return subtreeAddressPinPassed && driverUnbindPinPassed && subtreeProbeFixedPointPassed &&
1470 externalPassed && selfPassed;
1473#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1474bool UsbHub::runHostedInterruptOwnershipRegression() {
1475 const bool passed = runInterruptOwnershipRegression();
1477 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-interrupt-handle-cancel-drain");
1480 "HOSTED-WAIT-TEST: FAIL usb-interrupt-handle-cancel-drain: "
1481 "subtree address pin, external drain, or callback-context retry failed");
1486EXPORTED_PUBLIC
bool runHostedUsbInterruptOwnershipRegression() {
1487 return UsbHub::runHostedInterruptOwnershipRegression();
1491#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1492bool UsbHub::runQemuInterruptOwnershipRegression() {
1493 const bool passed = runInterruptOwnershipRegression();
1495 NOTICE(
"QEMU-CONCURRENCY-TEST: PASS usb-interrupt-cancel-drain-smp");
1498 "QEMU-CONCURRENCY-TEST: FAIL usb-interrupt-cancel-drain-smp: "
1499 "subtree address pin, external drain, callback-context retry, or CPU spread failed");
virtual Vector< Address * > & addresses()
@ UsbController
Same as Controller, but exposes USB devices.
void addChild(Device *pDevice)
Vector< Device * > m_Children
void setParent(Device *p)
void removeChild(size_t n)
bool test(size_t n) const
::Iterator< T, node_t > Iterator
static ProcessorInformation & information()
static Scheduler & instance()
MUST_USE_RESULT bool acquireWithError(size_t n, size_t timeoutSecs, size_t timeoutUsecs, SemaphoreError &error)
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Service * getService(const String &serviceName)
bool serve(const String &serviceName, ServiceFeatures::Type type, void *data, size_t dataLen)
UsbState getUsbState()
Returns the current state of the device.
ConfigDescriptor * getConfiguration()
Returns the configuration in use.
void useInterface(uint8_t nInterface)
Switches to the given interface.
uint8_t getPort()
Returns the number of the port on which the device is connected.
uint8_t getAddress()
Returns the current address of the device.
DeviceDescriptor * getDescriptor()
Returns the device descriptor of the device.
void initialise(uint8_t nAddress)
Initialises the device at the given address.
ssize_t doSync(uintptr_t nTransaction, uint32_t timeout=5000)
virtual void replaySuppressedConnectionChange(size_t port)
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)=0
Gets a UsbDevice from a given vendor:product pair.
void retainDisconnectedAddressesUntilControllerTeardown()
size_t m_RootPortGeneration
MUST_USE_RESULT bool suppressConnectionChanges(size_t port, ConnectionChangeSuppression &suppression)
static void syncCallback(uintptr_t pParam, ssize_t ret)
Callback used by synchronous transactions.
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)=0
bool deviceConnected(uint8_t nPort, UsbSpeed speed)
Called when a device is connected to a port on the hub.
void deviceDisconnected(uint8_t nPort)
Called when a device is disconnected from a port on the hub.
virtual bool portSpeed(uint8_t, UsbSpeed &)
virtual void releaseDeviceAddress(uint8_t)
virtual size_t currentRootPortGeneration(size_t port) const
MUST_USE_RESULT bool publishInterruptInHandle(UsbInterruptInHandle &handle, const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter)
RootConnection rootConnectionForChild(uint8_t childPort) const
size_t m_AddressReferences[128]
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)=0
MUST_USE_RESULT bool deferConnectionChangeIfSuppressed(size_t port)
void attachToUpstreamHub(UsbHub *upstream, const RootConnection &connection)
ExtensibleBitmap m_UsedAddresses
Atomic< size_t > m_ConnectionChangeStates[ConnectionChangePortCount]
bool m_RetainDisconnectedAddresses
void disconnectAllDevices()
MUST_USE_RESULT bool reset()
static UsbPnP & instance()
Singleton design.
A vector / dynamic array.
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
void pushBack(const T &value)
Structure used synchronous transactions.