The Pedigree Project 0.1
UsbPnP.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 "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"
31
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"
37#endif
38
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"
43#endif
44
46
48 CallbackItem(callback_t callback, uint16_t vendorId, uint16_t productId, uint8_t deviceClass,
49 uint8_t subclass, uint8_t protocol)
50 : callback(callback),
51 nVendorId(vendorId),
52 nProductId(productId),
53 nClass(deviceClass),
54 nSubclass(subclass),
55 nProtocol(protocol),
56 sequence(0),
57 next(nullptr),
58 operations(),
59 bindings(),
60 firstBinding(nullptr),
61 lastBinding(nullptr) {}
62
64 if (operations.isOpen())
65 operations.close();
66 operations.wait();
67 if (bindings.isOpen())
68 bindings.close();
69 bindings.wait();
70 assert(!firstBinding && !lastBinding);
71 }
72
73 callback_t callback;
74 uint16_t nVendorId;
75 uint16_t nProductId;
76 uint8_t nClass;
77 uint8_t nSubclass;
78 uint8_t nProtocol;
79 size_t sequence;
80 CallbackItem* next;
81 OperationBarrier operations;
82 OperationBarrier bindings;
83 UsbDeviceContainer* firstBinding;
84 UsbDeviceContainer* lastBinding;
85};
86
88 void* owner;
89 ActiveInvocation* next;
90};
91
92UsbPnP::Registration::Registration() : m_Owner(nullptr), m_Item(nullptr), m_Resetting(false) {}
93
94UsbPnP::Registration::Registration(Registration&& other)
95 : m_Owner(static_cast<UsbPnP*>(other.m_Owner)),
96 m_Item(static_cast<CallbackItem*>(other.m_Item)),
97 m_Resetting(false) {
98 assert(!other.m_Resetting);
99 other.m_Owner = nullptr;
100 other.m_Item = nullptr;
101}
102
103UsbPnP::Registration::~Registration() {
104 if (!reset()) {
105 FATAL("Live UsbPnP registration could not be retired.");
106 }
107}
108
109UsbPnP::Registration& UsbPnP::Registration::operator=(Registration&& other) {
110 if (this != &other) {
111 if (!reset()) {
112 FATAL("UsbPnP registration move could not retire ownership.");
113 }
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;
119 }
120 return *this;
121}
122
123bool UsbPnP::Registration::reset() {
124 while (!m_Resetting.compareAndSwap(false, true)) {
125 UsbPnP* owner = m_Owner;
126 if (owner && owner->inCurrentCallbackContext())
127 return false;
129 }
130
131 UsbPnP* owner = m_Owner;
132 CallbackItem* item = m_Item;
133 if (!owner || !item) {
134 m_Resetting = false;
135 return true;
136 }
137
138 if (!owner->unregisterCallback(item)) {
139 m_Resetting = false;
140 return false;
141 }
142
143 m_Owner = nullptr;
144 m_Item = nullptr;
145 m_Resetting = false;
146 return true;
147}
148
149void UsbPnP::Registration::adopt(UsbPnP* owner, CallbackItem* item) {
150 assert(!static_cast<CallbackItem*>(m_Item));
151 assert(!m_Resetting);
152 m_Owner = owner;
153 m_Item = item;
154}
155
156UsbPnP::UsbPnP()
157 : m_FirstCallback(nullptr),
158 m_LastCallback(nullptr),
159 m_CallbackCount(0),
160 m_CallbackLock(),
161 m_BindingLock(),
162 m_NextCallbackSequence(1),
163 m_ActiveInvocations(nullptr) {}
164
165UsbPnP::~UsbPnP() {
166 {
167 LockGuard<Spinlock> guard(m_CallbackLock);
168 if (isCallbackContext(currentInvocationOwner())) {
169 FATAL("UsbPnP cannot be destroyed from callback context.");
170 }
171 }
172
173 while (true) {
174 CallbackItem* item = nullptr;
175 {
176 LockGuard<Spinlock> guard(m_CallbackLock);
177 if (!m_FirstCallback) {
178 break;
179 }
180
181 item = m_FirstCallback;
182 m_FirstCallback = item->next;
183 if (!m_FirstCallback) {
184 m_LastCallback = nullptr;
185 }
186 if (item->operations.isOpen()) {
187 --m_CallbackCount;
188 }
189 item->operations.close();
190 }
191
192 item->operations.wait();
193 retireBindings(item, false);
194 delete item;
195 }
196}
197
198bool UsbPnP::probeDevice(Device* pDeviceBase) {
199 if (!pDeviceBase || pDeviceBase->getType() != Device::UsbContainer)
200 return false;
201 auto* container = static_cast<UsbDeviceContainer*>(pDeviceBase);
203 if (!container->tryAcquireProbe(probe))
204 return false;
205 return probeDeviceAdmitted(container, probe);
206}
207
208bool UsbPnP::probeDeviceAdmitted(UsbDeviceContainer* container, OperationBarrier::Lease& probe) {
209 if (!container || !probe)
210 return false;
211
212 LockGuard<Mutex> guard(container->m_ProbeLock);
213 doProbe(container);
214 UsbDevice* device = container->getUsbDevice();
215 return device && device->getUsbState() == UsbDevice::HasDriver;
216}
217
219 // Sanity check.
220 if (!(pDeviceBase->getType() == Device::UsbContainer)) {
221 return pDeviceBase;
222 }
223
224 UsbDeviceContainer* pContainer = static_cast<UsbDeviceContainer*>(pDeviceBase);
225 UsbDevice* pDevice = pContainer->getUsbDevice();
226
227 // Is this device already handled by a driver?
228 if (pDevice->getUsbState() == UsbDevice::HasDriver) {
229 return pDeviceBase;
230 }
231
232 UsbHub::StartupActivity startup(pDevice->getHub());
233 if (!startup)
234 return pDeviceBase;
235
236 const bool isHub = pDevice->getInterface()->nClass == 9;
237
238 size_t afterSequence = 0;
239 while (true) {
240 CallbackItem* item = nullptr;
241 callback_t callback = nullptr;
242 size_t sequence = 0;
243 ActiveInvocation invocation = {nullptr, nullptr};
244 if (!acquireCallback(pDevice, afterSequence, item, callback, sequence, invocation)) {
245 break;
246 }
247
248 // Call the callback, which will give us (hopefully) a copy of pDevice,
249 // in the form of a driver class
250 UsbDevice* pNewDevice = callback(pDevice);
251 afterSequence = sequence;
252
253 // Was this device rejected by the driver?
254 if (!pNewDevice) {
255 finishCallback(item, invocation);
256 startup.failed();
257 continue;
258 }
259 if (pNewDevice == pDevice) {
260 ERROR("USB: PnP factories must return a distinct driver instance");
261 finishCallback(item, invocation);
262 startup.failed();
263 continue;
264 }
265
266 // Initialise the driver
267 pNewDevice->initialiseDriver();
268
269 // Did the device go into the driver state?
270 if (pNewDevice->getUsbState() == UsbDevice::HasDriver) {
272 if (!item->bindings.tryAcquire(binding)) {
273 delete pNewDevice;
274 finishCallback(item, invocation);
275 ERROR("USB: PnP registration closed before its binding was published");
276 startup.failed();
277 return pDeviceBase;
278 }
279
280 // Publish into the already-linked container before releasing callback
281 // ownership, so registration teardown cannot miss the bound object.
282 const bool replaced = pContainer->replaceUsbDevice(pNewDevice);
283 if (replaced)
284 publishBinding(item, pContainer, binding);
285 // A hub is infrastructure: its children still have to become usable.
286 // Once a leaf driver is published, later failures cannot revoke it.
287 if (replaced && !isHub)
288 startup.deviceReady();
289 finishCallback(item, invocation);
290 if (!replaced) {
291 delete pNewDevice;
292 ERROR("USB: PnP could not publish a matched driver into its container");
293 startup.failed();
294 }
295 return pDeviceBase;
296 } else {
297 delete pNewDevice;
298 finishCallback(item, invocation);
299 startup.failed();
300 }
301 }
302 return pDeviceBase;
303}
304
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;
310 resultSequence = 0;
311
312 UsbDevice::DeviceDescriptor* descriptor = device->getDescriptor();
313 UsbDevice::Interface* interface = device->getInterface();
314
315 LockGuard<Spinlock> guard(m_CallbackLock);
316 for (CallbackItem* item = m_FirstCallback; item; item = item->next) {
317 if (item->sequence <= afterSequence) {
318 continue;
319 }
320
321 if ((item->nVendorId != VendorIdNone) && (item->nVendorId != descriptor->nVendorId)) {
322 continue;
323 }
324 if ((item->nProductId != ProductIdNone) && (item->nProductId != descriptor->nProductId)) {
325 continue;
326 }
327 if ((item->nClass != ClassNone) && (item->nClass != interface->nClass)) {
328 continue;
329 }
330 if ((item->nSubclass != SubclassNone) && (item->nSubclass != interface->nSubclass)) {
331 continue;
332 }
333 if ((item->nProtocol != ProtocolNone) && (item->nProtocol != interface->nProtocol)) {
334 continue;
335 }
336 if (!item->operations.tryEnter()) {
337 continue;
338 }
339
340 resultItem = item;
341 resultCallback = item->callback;
342 resultSequence = item->sequence;
343 invocation.owner = currentInvocationOwner();
344 invocation.next = m_ActiveInvocations;
345 m_ActiveInvocations = &invocation;
346 return true;
347 }
348
349 return false;
350}
351
352void UsbPnP::finishCallback(CallbackItem* item, ActiveInvocation& invocation) {
353 LockGuard<Spinlock> guard(m_CallbackLock);
354 ActiveInvocation** invocationLink = &m_ActiveInvocations;
355 while (*invocationLink && *invocationLink != &invocation) {
356 invocationLink = &((*invocationLink)->next);
357 }
358 if (*invocationLink) {
359 *invocationLink = invocation.next;
360 } else {
361 FATAL("UsbPnP lost an active callback invocation.");
362 }
363
364 item->operations.leave();
365}
366
367void* UsbPnP::currentInvocationOwner() {
368 ProcessorInformation& information = Processor::information();
369 Thread* thread = information.getCurrentThread();
370 return thread ? static_cast<void*>(thread) : static_cast<void*>(&information);
371}
372
373bool UsbPnP::isCallbackContext(void* owner) const {
374 for (ActiveInvocation* invocation = m_ActiveInvocations; invocation;
375 invocation = invocation->next) {
376 if (invocation->owner == owner) {
377 return true;
378 }
379 }
380 return false;
381}
382
383bool UsbPnP::inCurrentCallbackContext() {
384 LockGuard<Spinlock> guard(m_CallbackLock);
385 return isCallbackContext(currentInvocationOwner());
386}
387
389 struct ProbeCandidate {
390 explicit ProbeCandidate(UsbDeviceContainer* container) : container(container), probe() {}
391
392 UsbDeviceContainer* container;
394 };
395
396 List<ProbeCandidate*> candidates;
397 auto collectCandidate = [](Device* p, List<ProbeCandidate*>* candidates) -> Device* {
398 if (p->getType() == Device::UsbContainer) {
399 auto* candidate = new ProbeCandidate(static_cast<UsbDeviceContainer*>(p));
400 if (candidate->container->tryAcquireProbe(candidate->probe))
401 candidates->pushBack(candidate);
402 else
403 delete candidate;
404 }
405 return p;
406 };
407
408 auto collector = pedigree_std::make_callable(collectCandidate);
409 Device::foreach (collector, pParent, &candidates);
410
411 while (candidates.count()) {
412 ProbeCandidate* candidate = candidates.popFront();
413 probeDeviceAdmitted(candidate->container, candidate->probe);
414 delete candidate;
415 }
416}
417
418bool UsbPnP::registerCallbackItem(CallbackItem* item, Registration& registration, bool reprobe) {
419 if (!item || !item->callback || registration) {
420 delete item;
421 return false;
422 }
423
424 {
425 LockGuard<Spinlock> guard(m_CallbackLock);
426 item->sequence = m_NextCallbackSequence++;
427 if (m_LastCallback) {
428 m_LastCallback->next = item;
429 } else {
430 m_FirstCallback = item;
431 }
432 m_LastCallback = item;
433 ++m_CallbackCount;
434 registration.adopt(this, item);
435 }
436
437 if (reprobe) {
438 reprobeDevices(nullptr);
439 }
440 return true;
441}
442
443bool UsbPnP::registerCallback(uint16_t nVendorId, uint16_t nProductId, callback_t callback,
444 Registration& registration) {
445 return registerCallbackItem(
446 new CallbackItem(callback, nVendorId, nProductId, ClassNone, SubclassNone, ProtocolNone),
447 registration, true);
448}
449
450bool UsbPnP::registerCallback(uint8_t nClass, uint8_t nSubclass, uint8_t nProtocol,
451 callback_t callback, Registration& registration) {
452 return registerCallbackItem(
453 new CallbackItem(callback, VendorIdNone, ProductIdNone, nClass, nSubclass, nProtocol),
454 registration, true);
455}
456
457void UsbPnP::publishBinding(CallbackItem* item, UsbDeviceContainer* container,
458 OperationBarrier::Lease& binding) {
459 assert(item && container && binding);
460 LockGuard<Mutex> guard(m_BindingLock);
461 assert(!container->m_BindingOwner && !container->m_PreviousBinding && !container->m_NextBinding);
462
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;
468 else
469 item->firstBinding = container;
470 item->lastBinding = container;
471 container->m_BindingLease = pedigree_std::move(binding);
472}
473
474void UsbPnP::unlinkBindingLocked(UsbDeviceContainer* container) {
475 auto* item = static_cast<CallbackItem*>(container->m_BindingOwner);
476 if (!item) {
477 assert(!container->m_PreviousBinding && !container->m_NextBinding);
478 return;
479 }
480
481 if (container->m_PreviousBinding)
482 container->m_PreviousBinding->m_NextBinding = container->m_NextBinding;
483 else
484 item->firstBinding = container->m_NextBinding;
485 if (container->m_NextBinding)
486 container->m_NextBinding->m_PreviousBinding = container->m_PreviousBinding;
487 else
488 item->lastBinding = container->m_PreviousBinding;
489
490 container->m_BindingOwner = nullptr;
491 container->m_PreviousBinding = nullptr;
492 container->m_NextBinding = nullptr;
493 // Destruction treats this atomic pointer as the unlink-complete sentinel.
494 // Publish it only after every other access to the container has finished.
495 container->m_BindingRegistry = nullptr;
496}
497
498void UsbPnP::detachBinding(UsbDeviceContainer* container) {
499 if (!container)
500 return;
501 LockGuard<Mutex> guard(m_BindingLock);
502 unlinkBindingLocked(container);
503}
504
505void UsbPnP::retireBindings(CallbackItem* item, bool reprobe) {
506 if (!item)
507 return;
508 if (item->bindings.isOpen())
509 item->bindings.close();
510
511 struct BoundCandidate {
512 BoundCandidate() : container(nullptr), probe(), binding() {}
513
514 UsbDeviceContainer* container;
517 };
518
519 while (true) {
520 BoundCandidate candidate;
521 {
522 LockGuard<Mutex> bindingGuard(m_BindingLock);
523 candidate.container = item->firstBinding;
524 if (!candidate.container)
525 break;
526
527 const bool admitted = candidate.container->tryAcquireProbe(candidate.probe);
528 unlinkBindingLocked(candidate.container);
529 if (admitted)
530 candidate.binding = pedigree_std::move(candidate.container->m_BindingLease);
531 }
532
533 if (!candidate.probe) {
534 // Physical teardown already closed this container. It keeps the binding
535 // lease until its driver has been destroyed; bindings.wait() below is
536 // the retirement join.
537 continue;
538 }
539
540 {
541 // Keep the retiring registration alive through its driver's destructor,
542 // then release it before another callback can bind the generic device.
543 OperationBarrier::Lease binding = pedigree_std::move(candidate.binding);
544 Machine::setShutdownDetail("USB: locking bound device for retirement");
545 LockGuard<Mutex> probeGuard(candidate.container->m_ProbeLock);
546 UsbDevice* bound = candidate.container->m_pUsbDevice;
547 UsbDevice* generic = new UsbDevice(bound);
548 generic->m_UsbState = UsbDevice::HasInterface;
549 Machine::setShutdownDetail("USB: preparing bound driver for retirement");
551 Machine::setShutdownDetail("USB: replacing bound driver");
552 const bool replaced = candidate.container->replaceUsbDevice(generic);
553 assert(replaced);
554 (void)replaced;
555 }
556
557 if (reprobe) {
558 Machine::setShutdownDetail("USB: reprobe after driver retirement");
559 probeDeviceAdmitted(candidate.container, candidate.probe);
560 }
561 }
562
563 Machine::setShutdownDetail("USB: waiting for retired bindings");
564 item->bindings.wait();
565}
566
567bool UsbPnP::unregisterCallback(CallbackItem* item) {
568 bool found = false;
569 bool callbackContext = false;
570 {
571 LockGuard<Spinlock> guard(m_CallbackLock);
572 CallbackItem* previous = nullptr;
573 for (CallbackItem* current = m_FirstCallback; current; current = current->next) {
574 if (current != item) {
575 previous = current;
576 continue;
577 }
578
579 if (item->operations.isOpen()) {
580 item->operations.close();
581 --m_CallbackCount;
582 }
583 callbackContext = isCallbackContext(currentInvocationOwner());
584 if (!callbackContext) {
585 if (previous) {
586 previous->next = item->next;
587 } else {
588 m_FirstCallback = item->next;
589 }
590 if (m_LastCallback == item) {
591 m_LastCallback = previous;
592 }
593 item->next = nullptr;
594 }
595 found = true;
596 break;
597 }
598 }
599
600 if (!found) {
601 return true;
602 }
603 if (callbackContext) {
604 return false;
605 }
606 Machine::setShutdownDetail("USB: draining probe callbacks");
607 item->operations.wait();
608 retireBindings(item, true);
609 delete item;
610 return true;
611}
612
613#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
614namespace {
615constexpr bool PinTestThreads = HOSTED;
616
617struct HostedRegistrationContext {
618 HostedRegistrationContext(UsbPnP* registry, UsbPnP::Registration* registration)
619 : registry(registry),
620 registration(registration),
621 releaseCallback(0),
622 callbackEntered(0),
623 callbackFinished(0),
624 callbackResetRejected(0),
625 invocationFinished(0),
626 unregisterStarted(0),
627 unregisterFinished(0) {}
628
629 UsbPnP* registry;
630 UsbPnP::Registration* registration;
631 Semaphore releaseCallback;
632 Atomic<size_t> callbackEntered;
633 Atomic<size_t> callbackFinished;
634 Atomic<size_t> callbackResetRejected;
635 Atomic<size_t> invocationFinished;
636 Atomic<size_t> unregisterStarted;
637 Atomic<size_t> unregisterFinished;
638};
639
640HostedRegistrationContext* g_HostedRegistrationContext = nullptr;
641
642struct HostedSelfRemovalContext {
643 explicit HostedSelfRemovalContext(UsbPnP::Registration* registration)
644 : registration(registration), callbackEntered(0), resetRejected(0), resetFinished(0) {}
645
646 UsbPnP::Registration* registration;
647 Atomic<size_t> callbackEntered;
648 Atomic<size_t> resetRejected;
649 Atomic<size_t> resetFinished;
650};
651
652HostedSelfRemovalContext* g_HostedSelfRemovalContext = nullptr;
653
654struct HostedReciprocalRemovalContext {
655 HostedReciprocalRemovalContext(UsbPnP* registry, UsbPnP::Registration* first,
656 UsbPnP::Registration* second)
657 : registry(registry),
658 first(first),
659 second(second),
660 beginReset(0),
661 callbacksEntered(0),
662 resetRejections(0),
663 resetsFinished(0),
664 invocationsFinished(0),
665 firstProcessor(static_cast<size_t>(-1)),
666 secondProcessor(static_cast<size_t>(-1)),
667 failures(0) {}
668
669 UsbPnP* registry;
671 UsbPnP::Registration* second;
672 Semaphore beginReset;
673 Atomic<size_t> callbacksEntered;
674 Atomic<size_t> resetRejections;
675 Atomic<size_t> resetsFinished;
676 Atomic<size_t> invocationsFinished;
677 Atomic<size_t> firstProcessor;
678 Atomic<size_t> secondProcessor;
679 Atomic<size_t> failures;
680};
681
682HostedReciprocalRemovalContext* g_HostedReciprocalRemovalContext = nullptr;
683
684Atomic<size_t> g_BoundDriverDestructions(0);
685Atomic<size_t> g_BoundDriverRetirements(0);
686Atomic<size_t> g_UnrelatedTreeVisits(0);
687
688class BindingTreeAccess : public Device {
689 public:
690 static Device& rootDevice() {
691 return root();
692 }
693};
694
695class HostedUnrelatedTreeDevice : public Device {
696 public:
697 Type getType() override {
698 g_UnrelatedTreeVisits += 1;
699 return Generic;
700 }
701};
702
703class HostedBindingUsbDevice : public UsbDevice {
704 public:
705 HostedBindingUsbDevice() : UsbDevice(nullptr, 1, FullSpeed), m_IsDriver(false) {
706 auto* rawDevice =
707 reinterpret_cast<UsbDeviceDescriptor*>(new uint8_t[sizeof(UsbDeviceDescriptor)]);
708 ByteSet(rawDevice, 0, sizeof(UsbDeviceDescriptor));
709 rawDevice->nLength = sizeof(UsbDeviceDescriptor);
710 rawDevice->nType = UsbDescriptor::Device;
711 rawDevice->nConfigurations = 1;
712 m_pDescriptor = new DeviceDescriptor(rawDevice);
713
714 constexpr size_t ConfigBytes =
716 uint8_t* rawConfig = new uint8_t[ConfigBytes];
717 ByteSet(rawConfig, 0, ConfigBytes);
718 auto* config = reinterpret_cast<UsbConfigurationDescriptor*>(rawConfig);
719 config->nLength = sizeof(UsbConfigurationDescriptor);
720 config->nType = UsbDescriptor::Configuration;
721 config->nTotalLength = ConfigBytes;
722 config->nInterfaces = 1;
723 config->nConfig = 1;
724 auto* interface =
725 reinterpret_cast<UsbInterfaceDescriptor*>(rawConfig + sizeof(UsbConfigurationDescriptor));
726 interface->nLength = sizeof(UsbInterfaceDescriptor);
727 interface->nType = UsbDescriptor::Interface;
728 interface->nClass = 0xFE;
729
730 m_pConfiguration = new ConfigDescriptor(rawConfig, ConfigBytes, FullSpeed);
731 m_pDescriptor->configList.pushBack(m_pConfiguration);
732 m_pInterface = m_pConfiguration->interfaceList[0];
733 m_nAddress = 1;
734 m_UsbState = HasInterface;
735 }
736
737 explicit HostedBindingUsbDevice(UsbDevice* device) : UsbDevice(device), m_IsDriver(true) {}
738
739 ~HostedBindingUsbDevice() override {
740 if (m_IsDriver)
741 g_BoundDriverDestructions += 1;
742 }
743
744 void initialiseDriver() override {
745 m_UsbState = HasDriver;
746 }
747
748 void prepareForDriverRetirement() override {
749 if (m_IsDriver)
750 g_BoundDriverRetirements += 1;
751 }
752
753 private:
754 bool m_IsDriver;
755};
756
757UsbDevice* hostedBindingCallback(UsbDevice* device) {
758 return new HostedBindingUsbDevice(device);
759}
760
761UsbDevice* hostedRegistrationCallback(UsbDevice*) {
762 HostedRegistrationContext* context = g_HostedRegistrationContext;
763 context->callbackEntered += 1;
764 const bool released = context->releaseCallback.acquireForCompletion();
765 (void)released;
766 if (!context->registration->reset())
767 context->callbackResetRejected += 1;
768 context->callbackFinished += 1;
769 return nullptr;
770}
771
772UsbDevice* hostedSelfRemovalCallback(UsbDevice*) {
773 HostedSelfRemovalContext* context = g_HostedSelfRemovalContext;
774 context->callbackEntered += 1;
775 if (!context->registration->reset() && *context->registration) {
776 context->resetRejected += 1;
777 }
778 context->resetFinished += 1;
779 return nullptr;
780}
781
782UsbDevice* hostedFirstReciprocalRemovalCallback(UsbDevice*) {
783 HostedReciprocalRemovalContext* context = g_HostedReciprocalRemovalContext;
784 context->firstProcessor = Processor::id();
785 context->callbacksEntered += 1;
786 const bool released = context->beginReset.acquireForCompletion();
787 (void)released;
788 if (!context->second->reset() && *context->second) {
789 context->resetRejections += 1;
790 } else {
791 context->failures += 1;
792 }
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) {
797 }
798 if (context->resetsFinished != static_cast<size_t>(2)) {
799 context->failures += 1;
800 }
801 return nullptr;
802}
803
804UsbDevice* hostedSecondReciprocalRemovalCallback(UsbDevice*) {
805 HostedReciprocalRemovalContext* context = g_HostedReciprocalRemovalContext;
806 context->secondProcessor = Processor::id();
807 context->callbacksEntered += 1;
808 const bool released = context->beginReset.acquireForCompletion();
809 (void)released;
810 if (!context->first->reset() && *context->first) {
811 context->resetRejections += 1;
812 } else {
813 context->failures += 1;
814 }
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) {
819 }
820 if (context->resetsFinished != static_cast<size_t>(2)) {
821 context->failures += 1;
822 }
823 return nullptr;
824}
825
826int invokeHostedRegistration(void* parameter) {
827 HostedRegistrationContext* context = reinterpret_cast<HostedRegistrationContext*>(parameter);
828 if (context->registry->invokeCallbackForTest()) {
829 context->invocationFinished += 1;
830 }
831 return 0;
832}
833
834int unregisterHostedRegistration(void* parameter) {
835 HostedRegistrationContext* context = reinterpret_cast<HostedRegistrationContext*>(parameter);
836 context->unregisterStarted += 1;
837 context->registration->reset();
838 context->unregisterFinished += 1;
839 return 0;
840}
841
842int invokeFirstReciprocalRemoval(void* parameter) {
843 HostedReciprocalRemovalContext* context =
844 reinterpret_cast<HostedReciprocalRemovalContext*>(parameter);
845 if (context->registry->invokeCallbackForTest(0)) {
846 context->invocationsFinished += 1;
847 }
848 return 0;
849}
850
851int invokeSecondReciprocalRemoval(void* parameter) {
852 HostedReciprocalRemovalContext* context =
853 reinterpret_cast<HostedReciprocalRemovalContext*>(parameter);
854 if (context->registry->invokeCallbackForTest(1)) {
855 context->invocationsFinished += 1;
856 }
857 return 0;
858}
859
860bool waitForHostedValue(Atomic<size_t>& value, size_t expected) {
861 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
862 while (value != expected && Time::getTicks() < deadline) {
864 }
865 return value == expected;
866}
867} // namespace
868
869bool UsbPnP::invokeCallbackForTest(size_t callbackIndex) {
870 CallbackItem* item = nullptr;
871 callback_t callback = nullptr;
872 ActiveInvocation invocation = {nullptr, nullptr};
873 {
874 LockGuard<Spinlock> guard(m_CallbackLock);
875 item = m_FirstCallback;
876 while (item && callbackIndex) {
877 item = item->next;
878 --callbackIndex;
879 }
880 if (!item || !item->operations.tryEnter()) {
881 return false;
882 }
883 callback = item->callback;
884 invocation.owner = currentInvocationOwner();
885 invocation.next = m_ActiveInvocations;
886 m_ActiveInvocations = &invocation;
887 }
888
889 callback(nullptr);
890 finishCallback(item, invocation);
891 return true;
892}
893
894size_t UsbPnP::callbackCountForTest() {
895 LockGuard<Spinlock> guard(m_CallbackLock);
896 return m_CallbackCount;
897}
898
899bool UsbPnP::callbackStorageEmptyForTest() {
900 LockGuard<Spinlock> guard(m_CallbackLock);
901 return !m_FirstCallback && !m_LastCallback && !m_CallbackCount;
902}
903
904bool UsbPnP::runRegistrationRegression() {
905 UsbPnP registry;
906 Registration registration;
907 HostedRegistrationContext context(&registry, &registration);
908 g_HostedRegistrationContext = &context;
909
910 const bool registered = registry.registerCallbackItem(
911 new CallbackItem(hostedRegistrationCallback, VendorIdNone, ProductIdNone, ClassNone,
912 SubclassNone, ProtocolNone),
913 registration, false);
914
915 Thread* invoker = nullptr;
916 Thread* unregisterer = nullptr;
917 bool callbackEntered = false;
918 bool registrationRemoved = false;
919 bool unregisterBlocked = false;
920 bool lateInvocationRejected = false;
921
922 if (registered) {
923 Process* process = Scheduler::instance().getKernelProcess();
924 invoker =
925 new Thread(process, invokeHostedRegistration, &context, nullptr, false, PinTestThreads);
926 invoker->setName("hosted USB PnP callback");
927 callbackEntered = waitForHostedValue(context.callbackEntered, 1);
928
929 if (callbackEntered) {
930 unregisterer = new Thread(process, unregisterHostedRegistration, &context, nullptr, false,
931 PinTestThreads);
932 unregisterer->setName("hosted USB PnP unregister");
933
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) {
938 }
939
940 registrationRemoved = registry.callbackCountForTest() == 0;
941 unregisterBlocked =
942 registrationRemoved && context.unregisterFinished == static_cast<size_t>(0);
943 lateInvocationRejected = registrationRemoved && !registry.invokeCallbackForTest();
944 }
945 }
946
947 context.releaseCallback.release();
948 const bool invokerJoined = !invoker || invoker->joinForCompletion();
949 const bool unregistererJoined = !unregisterer || unregisterer->joinForCompletion();
950 if (registration) {
951 registration.reset();
952 }
953 g_HostedRegistrationContext = nullptr;
954
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);
962
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;
981
982 Registration first;
983 Registration second;
984 HostedReciprocalRemovalContext reciprocalContext(&registry, &first, &second);
985 g_HostedReciprocalRemovalContext = &reciprocalContext;
986 const bool firstRegistered = registry.registerCallbackItem(
987 new CallbackItem(hostedFirstReciprocalRemovalCallback, VendorIdNone, ProductIdNone, ClassNone,
988 SubclassNone, ProtocolNone),
989 first, false);
990 const bool secondRegistered = registry.registerCallbackItem(
991 new CallbackItem(hostedSecondReciprocalRemovalCallback, VendorIdNone, ProductIdNone,
992 ClassNone, SubclassNone, ProtocolNone),
993 second, false);
994
995 Thread* firstInvoker = nullptr;
996 Thread* secondInvoker = nullptr;
997 bool bothEntered = false;
998 if (firstRegistered && secondRegistered) {
999 Process* process = Scheduler::instance().getKernelProcess();
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);
1007 }
1008
1009 reciprocalContext.beginReset.release(2);
1010 const bool firstJoined = !firstInvoker || firstInvoker->joinForCompletion();
1011 const bool secondJoined = !secondInvoker || secondInvoker->joinForCompletion();
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
1027#endif
1028 ;
1029 g_HostedReciprocalRemovalContext = nullptr;
1030
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));
1035#endif
1036
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,
1042 ProtocolNone),
1043 bindingRegistration, false);
1044 auto* bindingContainer = new UsbDeviceContainer(new HostedBindingUsbDevice);
1045 auto* unrelatedDevice = new HostedUnrelatedTreeDevice;
1046 {
1047 Device::TreeLockGuard treeGuard;
1048 bindingContainer->setParent(&BindingTreeAccess::rootDevice());
1049 BindingTreeAccess::rootDevice().addChild(bindingContainer);
1050 unrelatedDevice->setParent(&BindingTreeAccess::rootDevice());
1051 BindingTreeAccess::rootDevice().addChild(unrelatedDevice);
1052 }
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;
1064 {
1065 Device::TreeLockGuard treeGuard;
1066 bindingContainer->closeProbeAdmission();
1067 BindingTreeAccess::rootDevice().removeChild(bindingContainer);
1068 bindingContainer->setParent(nullptr);
1069 BindingTreeAccess::rootDevice().removeChild(unrelatedDevice);
1070 unrelatedDevice->setParent(nullptr);
1071 }
1072 bindingContainer->waitForProbes();
1073 delete bindingContainer;
1074 delete unrelatedDevice;
1075 const bool bindingRetirementPassed =
1076 bindingRegistered && bindingPublished && bindingRetired && genericRestored;
1077
1078 return drainPassed && selfRemovalPassed && reciprocalRemovalPassed && bindingRetirementPassed;
1079}
1080
1081#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1082bool UsbPnP::runHostedRegistrationRegression() {
1083 const bool passed = runRegistrationRegression();
1084 if (passed) {
1085 NOTICE("HOSTED-WAIT-TEST: PASS usb-pnp-registration-drain");
1086 } else {
1087 ERROR(
1088 "HOSTED-WAIT-TEST: FAIL usb-pnp-registration-drain: "
1089 "callback drain, reciprocal removal, or bound-instance retirement failed");
1090 }
1091 return passed;
1092}
1093#endif
1094
1095#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1096bool UsbPnP::runQemuRegistrationRegression() {
1097 const bool passed = runRegistrationRegression();
1098 if (passed) {
1099 NOTICE("QEMU-CONCURRENCY-TEST: PASS usb-pnp-reciprocal-unregister-smp");
1100 } else {
1101 ERROR(
1102 "QEMU-CONCURRENCY-TEST: FAIL usb-pnp-reciprocal-unregister-smp: "
1103 "callback drain, reciprocal removal, bound-instance retirement, or CPU spread failed");
1104 }
1105 return passed;
1106}
1107#endif
1108
1109#endif
static void foreach(Callback callback, Device *root=0)
Definition Device.cc:107
@ UsbContainer
Definition Device.h:89
virtual Type getType()
Definition Device.h:178
Definition List.h:61
static void setShutdownDetail(const char *detail)
Definition Machine.cc:38
MUST_USE_RESULT bool tryAcquire(Lease &lease)
static ProcessorId id()
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
bool joinForCompletion()
Definition Thread.cc:2750
MUST_USE_RESULT bool replaceUsbDevice(UsbDevice *pDev)
Definition UsbDevice.cc:748
UsbState getUsbState()
Returns the current state of the device.
Definition UsbDevice.h:211
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.
Definition UsbDevice.h:185
virtual void prepareForDriverRetirement()
Definition UsbDevice.h:253
Interface * getInterface()
Returns the interface in use.
Definition UsbDevice.h:226
Device * doProbe(Device *pDeviceBase)
Definition UsbPnP.cc:218
MUST_USE_RESULT bool registerCallback(uint16_t nVendorId, uint16_t nProductId, callback_t callback, Registration &registration)
Register a callback for the given vendor and product IDs.
Definition UsbPnP.cc:443
void reprobeDevices(Device *pParent)
Goes down the device tree, reprobing every USB device.
Definition UsbPnP.cc:388
static UsbPnP m_Instance
Static instance.
Definition UsbPnP.h:152
bool probeDevice(Device *pDeviceBase)
Tries to find a suitable driver for the given USB device.
Definition UsbPnP.cc:198
UsbDevice *(* callback_t)(UsbDevice *)
Callback function type.
Definition UsbPnP.h:50
@ Dec
Definition Log.h:126
T popFront()
Definition List.h:330
size_t count() const
Definition List.h:212
void pushBack(const T &value)
Definition List.h:216