20#include "modules/system/usb/UsbPnP.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/machine/Device.h"
24#include "pedigree/kernel/machine/Machine.h"
25#include "pedigree/kernel/process/OperationBarrier.h"
26#include "pedigree/kernel/process/Scheduler.h"
27#include "pedigree/kernel/process/Thread.h"
28#include "pedigree/kernel/processor/Processor.h"
29#include "pedigree/kernel/utilities/List.h"
30#include "pedigree/kernel/utilities/utility.h"
32#include "modules/system/usb/UsbDevice.h"
33#include "modules/system/usb/UsbHub.h"
34#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
35#include "modules/system/usb/UsbConstants.h"
36#include "modules/system/usb/UsbDescriptors.h"
39#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
40#include "pedigree/kernel/Atomic.h"
41#include "pedigree/kernel/process/Semaphore.h"
42#include "pedigree/kernel/time/Time.h"
49 uint8_t subclass, uint8_t protocol)
52 nProductId(productId),
60 firstBinding(
nullptr),
61 lastBinding(
nullptr) {}
64 if (operations.isOpen())
67 if (bindings.isOpen())
70 assert(!firstBinding && !lastBinding);
92UsbPnP::Registration::Registration() : m_Owner(nullptr), m_Item(nullptr), m_Resetting(false) {}
94UsbPnP::Registration::Registration(Registration&& other)
95 : m_Owner(static_cast<
UsbPnP*>(other.m_Owner)),
96 m_Item(static_cast<CallbackItem*>(other.m_Item)),
98 assert(!other.m_Resetting);
99 other.m_Owner =
nullptr;
100 other.m_Item =
nullptr;
103UsbPnP::Registration::~Registration() {
105 FATAL(
"Live UsbPnP registration could not be retired.");
110 if (
this != &other) {
112 FATAL(
"UsbPnP registration move could not retire ownership.");
114 assert(!other.m_Resetting);
115 m_Owner =
static_cast<UsbPnP*
>(other.m_Owner);
116 m_Item =
static_cast<CallbackItem*
>(other.m_Item);
117 other.m_Owner =
nullptr;
118 other.m_Item =
nullptr;
123bool UsbPnP::Registration::reset() {
124 while (!m_Resetting.compareAndSwap(
false,
true)) {
126 if (owner && owner->inCurrentCallbackContext())
132 CallbackItem* item = m_Item;
133 if (!owner || !item) {
138 if (!owner->unregisterCallback(item)) {
149void UsbPnP::Registration::adopt(
UsbPnP* owner, CallbackItem* item) {
150 assert(!
static_cast<CallbackItem*
>(m_Item));
151 assert(!m_Resetting);
157 : m_FirstCallback(nullptr),
158 m_LastCallback(nullptr),
162 m_NextCallbackSequence(1),
163 m_ActiveInvocations(nullptr) {}
168 if (isCallbackContext(currentInvocationOwner())) {
169 FATAL(
"UsbPnP cannot be destroyed from callback context.");
174 CallbackItem* item =
nullptr;
177 if (!m_FirstCallback) {
181 item = m_FirstCallback;
182 m_FirstCallback = item->next;
183 if (!m_FirstCallback) {
184 m_LastCallback =
nullptr;
186 if (item->operations.isOpen()) {
189 item->operations.close();
192 item->operations.wait();
193 retireBindings(item,
false);
203 if (!container->tryAcquireProbe(probe))
205 return probeDeviceAdmitted(container, probe);
209 if (!container || !probe)
214 UsbDevice* device = container->getUsbDevice();
215 return device && device->getUsbState() == UsbDevice::HasDriver;
225 UsbDevice* pDevice = pContainer->getUsbDevice();
228 if (pDevice->
getUsbState() == UsbDevice::HasDriver) {
236 const bool isHub = pDevice->
getInterface()->nClass == 9;
238 size_t afterSequence = 0;
244 if (!acquireCallback(pDevice, afterSequence, item, callback, sequence, invocation)) {
250 UsbDevice* pNewDevice = callback(pDevice);
251 afterSequence = sequence;
255 finishCallback(item, invocation);
259 if (pNewDevice == pDevice) {
260 ERROR(
"USB: PnP factories must return a distinct driver instance");
261 finishCallback(item, invocation);
270 if (pNewDevice->
getUsbState() == UsbDevice::HasDriver) {
274 finishCallback(item, invocation);
275 ERROR(
"USB: PnP registration closed before its binding was published");
284 publishBinding(item, pContainer, binding);
287 if (replaced && !isHub)
288 startup.deviceReady();
289 finishCallback(item, invocation);
292 ERROR(
"USB: PnP could not publish a matched driver into its container");
298 finishCallback(item, invocation);
305bool UsbPnP::acquireCallback(
UsbDevice* device,
size_t afterSequence, CallbackItem*& resultItem,
306 callback_t& resultCallback,
size_t& resultSequence,
307 ActiveInvocation& invocation) {
308 resultItem =
nullptr;
309 resultCallback =
nullptr;
316 for (CallbackItem* item = m_FirstCallback; item; item = item->next) {
317 if (item->sequence <= afterSequence) {
321 if ((item->nVendorId != VendorIdNone) && (item->nVendorId != descriptor->nVendorId)) {
324 if ((item->nProductId != ProductIdNone) && (item->nProductId != descriptor->nProductId)) {
327 if ((item->nClass != ClassNone) && (item->nClass != interface->nClass)) {
330 if ((item->nSubclass != SubclassNone) && (item->nSubclass != interface->nSubclass)) {
333 if ((item->nProtocol != ProtocolNone) && (item->nProtocol != interface->nProtocol)) {
336 if (!item->operations.tryEnter()) {
341 resultCallback = item->callback;
342 resultSequence = item->sequence;
343 invocation.owner = currentInvocationOwner();
344 invocation.next = m_ActiveInvocations;
345 m_ActiveInvocations = &invocation;
352void UsbPnP::finishCallback(CallbackItem* item, ActiveInvocation& invocation) {
354 ActiveInvocation** invocationLink = &m_ActiveInvocations;
355 while (*invocationLink && *invocationLink != &invocation) {
356 invocationLink = &((*invocationLink)->next);
358 if (*invocationLink) {
359 *invocationLink = invocation.next;
361 FATAL(
"UsbPnP lost an active callback invocation.");
364 item->operations.leave();
367void* UsbPnP::currentInvocationOwner() {
369 Thread* thread = information.getCurrentThread();
370 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
373bool UsbPnP::isCallbackContext(
void* owner)
const {
374 for (ActiveInvocation* invocation = m_ActiveInvocations; invocation;
375 invocation = invocation->next) {
376 if (invocation->owner == owner) {
383bool UsbPnP::inCurrentCallbackContext() {
385 return isCallbackContext(currentInvocationOwner());
389 struct ProbeCandidate {
390 explicit ProbeCandidate(
UsbDeviceContainer* container) : container(container), probe() {}
400 if (candidate->container->tryAcquireProbe(candidate->probe))
408 auto collector = pedigree_std::make_callable(collectCandidate);
411 while (candidates.
count()) {
412 ProbeCandidate* candidate = candidates.
popFront();
413 probeDeviceAdmitted(candidate->container, candidate->probe);
418bool UsbPnP::registerCallbackItem(CallbackItem* item, Registration& registration,
bool reprobe) {
419 if (!item || !item->callback || registration) {
426 item->sequence = m_NextCallbackSequence++;
427 if (m_LastCallback) {
428 m_LastCallback->next = item;
430 m_FirstCallback = item;
432 m_LastCallback = item;
434 registration.adopt(
this, item);
445 return registerCallbackItem(
446 new CallbackItem(callback, nVendorId, nProductId, ClassNone, SubclassNone, ProtocolNone),
452 return registerCallbackItem(
453 new CallbackItem(callback, VendorIdNone, ProductIdNone, nClass, nSubclass, nProtocol),
459 assert(item && container && binding);
461 assert(!container->m_BindingOwner && !container->m_PreviousBinding && !container->m_NextBinding);
463 container->m_BindingRegistry =
this;
464 container->m_BindingOwner = item;
465 container->m_PreviousBinding = item->lastBinding;
466 if (item->lastBinding)
467 item->lastBinding->m_NextBinding = container;
469 item->firstBinding = container;
470 item->lastBinding = container;
471 container->m_BindingLease = pedigree_std::move(binding);
475 auto* item =
static_cast<CallbackItem*
>(container->m_BindingOwner);
477 assert(!container->m_PreviousBinding && !container->m_NextBinding);
481 if (container->m_PreviousBinding)
482 container->m_PreviousBinding->m_NextBinding = container->m_NextBinding;
484 item->firstBinding = container->m_NextBinding;
485 if (container->m_NextBinding)
486 container->m_NextBinding->m_PreviousBinding = container->m_PreviousBinding;
488 item->lastBinding = container->m_PreviousBinding;
490 container->m_BindingOwner =
nullptr;
491 container->m_PreviousBinding =
nullptr;
492 container->m_NextBinding =
nullptr;
495 container->m_BindingRegistry =
nullptr;
502 unlinkBindingLocked(container);
505void UsbPnP::retireBindings(CallbackItem* item,
bool reprobe) {
508 if (item->bindings.isOpen())
509 item->bindings.close();
511 struct BoundCandidate {
512 BoundCandidate() : container(nullptr), probe(), binding() {}
520 BoundCandidate candidate;
523 candidate.container = item->firstBinding;
524 if (!candidate.container)
527 const bool admitted = candidate.container->tryAcquireProbe(candidate.probe);
528 unlinkBindingLocked(candidate.container);
530 candidate.binding = pedigree_std::move(candidate.container->m_BindingLease);
533 if (!candidate.probe) {
546 UsbDevice* bound = candidate.container->m_pUsbDevice;
548 generic->m_UsbState = UsbDevice::HasInterface;
552 const bool replaced = candidate.container->replaceUsbDevice(generic);
559 probeDeviceAdmitted(candidate.container, candidate.probe);
564 item->bindings.wait();
567bool UsbPnP::unregisterCallback(CallbackItem* item) {
569 bool callbackContext =
false;
572 CallbackItem* previous =
nullptr;
573 for (CallbackItem* current = m_FirstCallback; current; current = current->next) {
574 if (current != item) {
579 if (item->operations.isOpen()) {
580 item->operations.close();
583 callbackContext = isCallbackContext(currentInvocationOwner());
584 if (!callbackContext) {
586 previous->next = item->next;
588 m_FirstCallback = item->next;
590 if (m_LastCallback == item) {
591 m_LastCallback = previous;
593 item->next =
nullptr;
603 if (callbackContext) {
607 item->operations.wait();
608 retireBindings(item,
true);
613#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
615constexpr bool PinTestThreads = HOSTED;
617struct HostedRegistrationContext {
619 : registry(registry),
620 registration(registration),
624 callbackResetRejected(0),
625 invocationFinished(0),
626 unregisterStarted(0),
627 unregisterFinished(0) {}
640HostedRegistrationContext* g_HostedRegistrationContext =
nullptr;
642struct HostedSelfRemovalContext {
644 : registration(registration), callbackEntered(0), resetRejected(0), resetFinished(0) {}
652HostedSelfRemovalContext* g_HostedSelfRemovalContext =
nullptr;
654struct HostedReciprocalRemovalContext {
657 : registry(registry),
664 invocationsFinished(0),
665 firstProcessor(static_cast<size_t>(-1)),
666 secondProcessor(static_cast<size_t>(-1)),
682HostedReciprocalRemovalContext* g_HostedReciprocalRemovalContext =
nullptr;
688class BindingTreeAccess :
public Device {
690 static Device& rootDevice() {
695class HostedUnrelatedTreeDevice :
public Device {
697 Type getType()
override {
698 g_UnrelatedTreeVisits += 1;
703class HostedBindingUsbDevice :
public UsbDevice {
705 HostedBindingUsbDevice() :
UsbDevice(nullptr, 1, FullSpeed), m_IsDriver(false) {
710 rawDevice->nType = UsbDescriptor::Device;
711 rawDevice->nConfigurations = 1;
712 m_pDescriptor =
new DeviceDescriptor(rawDevice);
714 constexpr size_t ConfigBytes =
716 uint8_t* rawConfig =
new uint8_t[ConfigBytes];
717 ByteSet(rawConfig, 0, ConfigBytes);
720 config->nType = UsbDescriptor::Configuration;
721 config->nTotalLength = ConfigBytes;
722 config->nInterfaces = 1;
727 interface->nType = UsbDescriptor::Interface;
728 interface->nClass = 0xFE;
730 m_pConfiguration =
new ConfigDescriptor(rawConfig, ConfigBytes, FullSpeed);
731 m_pDescriptor->configList.pushBack(m_pConfiguration);
732 m_pInterface = m_pConfiguration->interfaceList[0];
734 m_UsbState = HasInterface;
737 explicit HostedBindingUsbDevice(
UsbDevice* device) :
UsbDevice(device), m_IsDriver(true) {}
739 ~HostedBindingUsbDevice()
override {
741 g_BoundDriverDestructions += 1;
744 void initialiseDriver()
override {
745 m_UsbState = HasDriver;
748 void prepareForDriverRetirement()
override {
750 g_BoundDriverRetirements += 1;
758 return new HostedBindingUsbDevice(device);
762 HostedRegistrationContext* context = g_HostedRegistrationContext;
763 context->callbackEntered += 1;
764 const bool released = context->releaseCallback.acquireForCompletion();
766 if (!context->registration->reset())
767 context->callbackResetRejected += 1;
768 context->callbackFinished += 1;
773 HostedSelfRemovalContext* context = g_HostedSelfRemovalContext;
774 context->callbackEntered += 1;
775 if (!context->registration->reset() && *context->registration) {
776 context->resetRejected += 1;
778 context->resetFinished += 1;
783 HostedReciprocalRemovalContext* context = g_HostedReciprocalRemovalContext;
785 context->callbacksEntered += 1;
786 const bool released = context->beginReset.acquireForCompletion();
788 if (!context->second->reset() && *context->second) {
789 context->resetRejections += 1;
791 context->failures += 1;
793 context->resetsFinished += 1;
794 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
795 while (context->resetsFinished !=
static_cast<size_t>(2) && Time::getTicks() < deadline) {
798 if (context->resetsFinished !=
static_cast<size_t>(2)) {
799 context->failures += 1;
805 HostedReciprocalRemovalContext* context = g_HostedReciprocalRemovalContext;
807 context->callbacksEntered += 1;
808 const bool released = context->beginReset.acquireForCompletion();
810 if (!context->first->reset() && *context->first) {
811 context->resetRejections += 1;
813 context->failures += 1;
815 context->resetsFinished += 1;
816 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
817 while (context->resetsFinished !=
static_cast<size_t>(2) && Time::getTicks() < deadline) {
820 if (context->resetsFinished !=
static_cast<size_t>(2)) {
821 context->failures += 1;
826int invokeHostedRegistration(
void* parameter) {
827 HostedRegistrationContext* context =
reinterpret_cast<HostedRegistrationContext*
>(parameter);
828 if (context->registry->invokeCallbackForTest()) {
829 context->invocationFinished += 1;
834int unregisterHostedRegistration(
void* parameter) {
835 HostedRegistrationContext* context =
reinterpret_cast<HostedRegistrationContext*
>(parameter);
836 context->unregisterStarted += 1;
837 context->registration->reset();
838 context->unregisterFinished += 1;
842int invokeFirstReciprocalRemoval(
void* parameter) {
843 HostedReciprocalRemovalContext* context =
844 reinterpret_cast<HostedReciprocalRemovalContext*
>(parameter);
845 if (context->registry->invokeCallbackForTest(0)) {
846 context->invocationsFinished += 1;
851int invokeSecondReciprocalRemoval(
void* parameter) {
852 HostedReciprocalRemovalContext* context =
853 reinterpret_cast<HostedReciprocalRemovalContext*
>(parameter);
854 if (context->registry->invokeCallbackForTest(1)) {
855 context->invocationsFinished += 1;
861 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
862 while (value != expected && Time::getTicks() < deadline) {
865 return value == expected;
869bool UsbPnP::invokeCallbackForTest(
size_t callbackIndex) {
870 CallbackItem* item =
nullptr;
872 ActiveInvocation invocation = {
nullptr,
nullptr};
875 item = m_FirstCallback;
876 while (item && callbackIndex) {
880 if (!item || !item->operations.tryEnter()) {
883 callback = item->callback;
884 invocation.owner = currentInvocationOwner();
885 invocation.next = m_ActiveInvocations;
886 m_ActiveInvocations = &invocation;
890 finishCallback(item, invocation);
894size_t UsbPnP::callbackCountForTest() {
896 return m_CallbackCount;
899bool UsbPnP::callbackStorageEmptyForTest() {
901 return !m_FirstCallback && !m_LastCallback && !m_CallbackCount;
904bool UsbPnP::runRegistrationRegression() {
906 Registration registration;
907 HostedRegistrationContext context(®istry, ®istration);
908 g_HostedRegistrationContext = &context;
910 const bool registered = registry.registerCallbackItem(
911 new CallbackItem(hostedRegistrationCallback, VendorIdNone, ProductIdNone, ClassNone,
912 SubclassNone, ProtocolNone),
913 registration,
false);
915 Thread* invoker =
nullptr;
916 Thread* unregisterer =
nullptr;
917 bool callbackEntered =
false;
918 bool registrationRemoved =
false;
919 bool unregisterBlocked =
false;
920 bool lateInvocationRejected =
false;
925 new Thread(process, invokeHostedRegistration, &context,
nullptr,
false, PinTestThreads);
926 invoker->setName(
"hosted USB PnP callback");
927 callbackEntered = waitForHostedValue(context.callbackEntered, 1);
929 if (callbackEntered) {
930 unregisterer =
new Thread(process, unregisterHostedRegistration, &context,
nullptr,
false,
932 unregisterer->setName(
"hosted USB PnP unregister");
934 const bool unregisterStarted = waitForHostedValue(context.unregisterStarted, 1);
935 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
936 while (unregisterStarted && registry.callbackCountForTest() && Time::getTicks() < deadline) {
940 registrationRemoved = registry.callbackCountForTest() == 0;
942 registrationRemoved && context.unregisterFinished ==
static_cast<size_t>(0);
943 lateInvocationRejected = registrationRemoved && !registry.invokeCallbackForTest();
947 context.releaseCallback.release();
949 const bool unregistererJoined = !unregisterer || unregisterer->
joinForCompletion();
951 registration.reset();
953 g_HostedRegistrationContext =
nullptr;
955 const bool drainPassed = registered && callbackEntered && registrationRemoved &&
956 unregisterBlocked && lateInvocationRejected && invokerJoined &&
957 unregistererJoined &&
958 context.callbackFinished ==
static_cast<size_t>(1) &&
959 context.callbackResetRejected ==
static_cast<size_t>(1) &&
960 context.invocationFinished ==
static_cast<size_t>(1) &&
961 context.unregisterFinished ==
static_cast<size_t>(1);
963 Registration selfRegistration;
964 HostedSelfRemovalContext selfContext(&selfRegistration);
965 g_HostedSelfRemovalContext = &selfContext;
966 const bool selfRegistered = registry.registerCallbackItem(
967 new CallbackItem(hostedSelfRemovalCallback, VendorIdNone, ProductIdNone, ClassNone,
968 SubclassNone, ProtocolNone),
969 selfRegistration,
false);
970 const bool selfInvoked = selfRegistered && registry.invokeCallbackForTest();
971 const bool selfOwnershipPreserved = selfInvoked && selfRegistration &&
972 registry.callbackCountForTest() == 0 &&
973 !registry.callbackStorageEmptyForTest() &&
974 selfContext.callbackEntered ==
static_cast<size_t>(1) &&
975 selfContext.resetRejected ==
static_cast<size_t>(1) &&
976 selfContext.resetFinished ==
static_cast<size_t>(1);
977 const bool selfRetired = selfRegistration && selfRegistration.reset();
978 const bool selfRemovalPassed = selfOwnershipPreserved && selfRetired && !selfRegistration &&
979 registry.callbackStorageEmptyForTest();
980 g_HostedSelfRemovalContext =
nullptr;
984 HostedReciprocalRemovalContext reciprocalContext(®istry, &first, &second);
985 g_HostedReciprocalRemovalContext = &reciprocalContext;
986 const bool firstRegistered = registry.registerCallbackItem(
987 new CallbackItem(hostedFirstReciprocalRemovalCallback, VendorIdNone, ProductIdNone, ClassNone,
988 SubclassNone, ProtocolNone),
990 const bool secondRegistered = registry.registerCallbackItem(
991 new CallbackItem(hostedSecondReciprocalRemovalCallback, VendorIdNone, ProductIdNone,
992 ClassNone, SubclassNone, ProtocolNone),
995 Thread* firstInvoker =
nullptr;
996 Thread* secondInvoker =
nullptr;
997 bool bothEntered =
false;
998 if (firstRegistered && secondRegistered) {
1000 firstInvoker =
new Thread(process, invokeFirstReciprocalRemoval, &reciprocalContext,
nullptr,
1001 false, PinTestThreads);
1002 firstInvoker->setName(
"hosted USB PnP reciprocal callback A");
1003 secondInvoker =
new Thread(process, invokeSecondReciprocalRemoval, &reciprocalContext,
nullptr,
1004 false, PinTestThreads);
1005 secondInvoker->setName(
"hosted USB PnP reciprocal callback B");
1006 bothEntered = waitForHostedValue(reciprocalContext.callbacksEntered, 2);
1009 reciprocalContext.beginReset.release(2);
1012 const bool registrationsPreserved = first && second;
1013 const bool admissionClosed = registry.callbackCountForTest() == 0 &&
1014 !registry.callbackStorageEmptyForTest() &&
1015 !registry.invokeCallbackForTest();
1016 const bool firstRetired = first && first.reset();
1017 const bool secondRetired = second && second.reset();
1018 const bool reciprocalRemovalPassed =
1019 firstRegistered && secondRegistered && bothEntered && firstJoined && secondJoined &&
1020 registrationsPreserved && admissionClosed && firstRetired && secondRetired && !first &&
1021 !second && registry.callbackStorageEmptyForTest() && !reciprocalContext.failures &&
1022 reciprocalContext.resetRejections ==
static_cast<size_t>(2) &&
1023 reciprocalContext.resetsFinished ==
static_cast<size_t>(2) &&
1024 reciprocalContext.invocationsFinished ==
static_cast<size_t>(2)
1025#
if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1026 && reciprocalContext.firstProcessor != reciprocalContext.secondProcessor
1029 g_HostedReciprocalRemovalContext =
nullptr;
1031#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1032 NOTICE(
"QEMU-CONCURRENCY-TEST: usb-pnp reciprocal cpus="
1033 <<
Dec <<
static_cast<size_t>(reciprocalContext.firstProcessor) <<
"/"
1034 <<
static_cast<size_t>(reciprocalContext.secondProcessor));
1037 const size_t destructionsBefore = g_BoundDriverDestructions;
1038 const size_t retirementsBefore = g_BoundDriverRetirements;
1039 Registration bindingRegistration;
1040 const bool bindingRegistered = registry.registerCallbackItem(
1041 new CallbackItem(hostedBindingCallback, VendorIdNone, ProductIdNone, ClassNone, SubclassNone,
1043 bindingRegistration,
false);
1045 auto* unrelatedDevice =
new HostedUnrelatedTreeDevice;
1048 bindingContainer->setParent(&BindingTreeAccess::rootDevice());
1049 BindingTreeAccess::rootDevice().addChild(bindingContainer);
1050 unrelatedDevice->setParent(&BindingTreeAccess::rootDevice());
1051 BindingTreeAccess::rootDevice().addChild(unrelatedDevice);
1053 const bool bindingPublished =
1054 bindingRegistered && registry.
probeDevice(bindingContainer) &&
1055 bindingContainer->getUsbDevice()->getUsbState() == UsbDevice::HasDriver;
1056 const size_t unrelatedVisitsBefore = g_UnrelatedTreeVisits;
1057 const bool bindingRetired = bindingPublished && bindingRegistration.reset();
1058 const bool genericRestored =
1059 bindingRetired && !bindingRegistration &&
1060 bindingContainer->getUsbDevice()->getUsbState() == UsbDevice::HasInterface &&
1061 g_BoundDriverRetirements == retirementsBefore + 1 &&
1062 g_BoundDriverDestructions == destructionsBefore + 1 &&
1063 g_UnrelatedTreeVisits == unrelatedVisitsBefore;
1066 bindingContainer->closeProbeAdmission();
1067 BindingTreeAccess::rootDevice().removeChild(bindingContainer);
1068 bindingContainer->setParent(
nullptr);
1069 BindingTreeAccess::rootDevice().removeChild(unrelatedDevice);
1070 unrelatedDevice->setParent(
nullptr);
1072 bindingContainer->waitForProbes();
1073 delete bindingContainer;
1074 delete unrelatedDevice;
1075 const bool bindingRetirementPassed =
1076 bindingRegistered && bindingPublished && bindingRetired && genericRestored;
1078 return drainPassed && selfRemovalPassed && reciprocalRemovalPassed && bindingRetirementPassed;
1081#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1082bool UsbPnP::runHostedRegistrationRegression() {
1083 const bool passed = runRegistrationRegression();
1085 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-pnp-registration-drain");
1088 "HOSTED-WAIT-TEST: FAIL usb-pnp-registration-drain: "
1089 "callback drain, reciprocal removal, or bound-instance retirement failed");
1095#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1096bool UsbPnP::runQemuRegistrationRegression() {
1097 const bool passed = runRegistrationRegression();
1099 NOTICE(
"QEMU-CONCURRENCY-TEST: PASS usb-pnp-reciprocal-unregister-smp");
1102 "QEMU-CONCURRENCY-TEST: FAIL usb-pnp-reciprocal-unregister-smp: "
1103 "callback drain, reciprocal removal, bound-instance retirement, or CPU spread failed");
static void foreach(Callback callback, Device *root=0)
static void setShutdownDetail(const char *detail)
MUST_USE_RESULT bool tryAcquire(Lease &lease)
static ProcessorInformation & information()
static Scheduler & instance()
MUST_USE_RESULT bool replaceUsbDevice(UsbDevice *pDev)
UsbState getUsbState()
Returns the current state of the device.
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.
virtual void prepareForDriverRetirement()
Interface * getInterface()
Returns the interface in use.
Device * doProbe(Device *pDeviceBase)
MUST_USE_RESULT bool registerCallback(uint16_t nVendorId, uint16_t nProductId, callback_t callback, Registration ®istration)
Register a callback for the given vendor and product IDs.
void reprobeDevices(Device *pParent)
Goes down the device tree, reprobing every USB device.
static UsbPnP m_Instance
Static instance.
bool probeDevice(Device *pDeviceBase)
Tries to find a suitable driver for the given USB device.
UsbDevice *(* callback_t)(UsbDevice *)
Callback function type.
void pushBack(const T &value)