The Pedigree Project 0.1
UsbHub.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/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"
27
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"
36#endif
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"
42
43#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
44namespace {
45Atomic<size_t> g_HostedSyncParamDestructions(0);
46void (*g_HostedSyncTimeoutHook)() = nullptr;
47UsbHub::ConnectionChangePendingHook g_ConnectionChangePendingHook = nullptr;
48UsbHub::ConnectionChangeReplayWaitHook g_ConnectionChangeReplayWaitHook = nullptr;
49} // namespace
50#endif
51
52UsbInterruptInHandle::UsbInterruptInHandle()
53 : m_Owner(nullptr),
54 m_Resetting(false),
55 m_Token{static_cast<uintptr_t>(-1), 0},
56 m_Callback(nullptr),
57 m_Parameter(0),
58 m_CancellationStarted(false) {}
59
60UsbInterruptInHandle::UsbInterruptInHandle(UsbInterruptInHandle&& other)
61 : m_Owner(static_cast<UsbHub*>(other.m_Owner)),
62 m_Resetting(false),
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;
73}
74
75UsbInterruptInHandle::~UsbInterruptInHandle() {
76 if (!reset())
77 panic("USB interrupt-IN handle destroyed from its running callback");
78}
79
80UsbInterruptInHandle& UsbInterruptInHandle::operator=(UsbInterruptInHandle&& other) {
81 if (this != &other) {
82 if (!reset())
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;
95 }
96 return *this;
97}
98
100 while (!m_Resetting.compareAndSwap(false, true)) {
101 UsbHub* owner = m_Owner;
102 if (owner && owner->inInterruptCallbackContext())
103 return false;
105 }
106
107 UsbHub* owner = m_Owner;
108 if (!owner) {
109 m_Resetting = false;
110 return true;
111 }
112
113 if (!owner->cancelInterruptInAndDrain(m_Token, m_Callback, m_Parameter, m_CancellationStarted)) {
114 m_CancellationStarted = true;
115 m_Resetting = false;
116 return false;
117 }
118
119 m_Owner = nullptr;
120 m_Token = {static_cast<uintptr_t>(-1), 0};
121 m_Callback = nullptr;
122 m_Parameter = 0;
123 m_CancellationStarted = false;
124 m_Resetting = false;
125 return true;
126}
127
128void UsbInterruptInHandle::adopt(UsbHub* owner, const UsbInterruptInToken& token,
129 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter) {
130 assert(!static_cast<UsbHub*>(m_Owner));
131 assert(!m_Resetting);
132 m_Token = token;
133 m_Callback = callback;
134 m_Parameter = parameter;
135 m_CancellationStarted = false;
136 // m_Owner is the publication sentinel observed by reset and destruction.
137 m_Owner = owner;
138}
139
140UsbHub::ConnectionChangeSuppression::ConnectionChangeSuppression() : m_Hub(nullptr), m_Port(0) {}
141
142UsbHub::ConnectionChangeSuppression::ConnectionChangeSuppression(UsbHub* hub, size_t port)
143 : m_Hub(hub), m_Port(port) {}
144
145UsbHub::ConnectionChangeSuppression::ConnectionChangeSuppression(
146 ConnectionChangeSuppression&& other)
147 : m_Hub(other.m_Hub), m_Port(other.m_Port) {
148 other.m_Hub = nullptr;
149 other.m_Port = 0;
150}
151
152UsbHub::ConnectionChangeSuppression::~ConnectionChangeSuppression() {
153 reset();
154}
155
156UsbHub::ConnectionChangeSuppression& UsbHub::ConnectionChangeSuppression::operator=(
157 ConnectionChangeSuppression&& other) {
158 if (this != &other) {
159 reset();
160 m_Hub = other.m_Hub;
161 m_Port = other.m_Port;
162 other.m_Hub = nullptr;
163 other.m_Port = 0;
164 }
165 return *this;
166}
167
168void UsbHub::ConnectionChangeSuppression::reset() {
169 if (m_Hub) {
170 UsbHub* hub = m_Hub;
171 const size_t port = m_Port;
172 m_Hub = nullptr;
173 m_Port = 0;
174 hub->releaseConnectionChangeSuppression(port);
175 }
176}
177
178UsbHub::UsbHub()
179 : m_RootHub(nullptr), m_IsRootHub(false), m_RootPort(0xff), m_RootPortGeneration(0) {
180 m_UsedAddresses.set(0);
181 m_AddressReferences[0] = 1;
182}
183
184UsbHub::UsbHub(Device* p)
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;
188}
189
190UsbHub::~UsbHub() {}
191
192UsbHub::StartupActivity::StartupActivity(UsbHub* hub)
193 : m_Root(hub ? hub->m_RootHub : nullptr),
194 m_Admitted(!m_Root || m_Root->beginStartupActivity()) {}
195
196UsbHub::StartupActivity::~StartupActivity() {
197 if (m_Root && m_Admitted)
198 m_Root->endStartupActivity(m_Outcome);
199}
200
202 if (m_IsRootHub)
203 return {childPort, currentRootPortGeneration(childPort)};
205}
206
208 const UsbInterruptInToken& token,
209 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter) {
210 if (handle || !callback || token.transaction == static_cast<uintptr_t>(-1) || !token.generation)
211 return false;
212
213 handle.adopt(this, token, callback, parameter);
214 return true;
215}
216
218 for (size_t i = 0; i < m_Children.count();) {
219 Device* child = m_Children[i];
220 if (!child || child->getType() != Device::UsbContainer) {
221 ++i;
222 continue;
223 }
224
225 auto* container = static_cast<UsbDeviceContainer*>(child);
226 UsbDevice* device = container->getUsbDevice();
227 if (device) {
228 deviceDisconnected(device->getPort());
229 } else {
230 {
231 Device::TreeLockGuard treeGuard;
232 container->closeProbeAdmission();
233 removeChild(i);
234 child->setParent(nullptr);
235 }
236 container->waitForProbes();
237 delete container;
238 }
239 }
240}
241
245
249
250void UsbHub::attachToUpstreamHub(UsbHub* upstream, const RootConnection& connection) {
251 if (m_IsRootHub || !upstream || !upstream->m_RootHub || connection.port == 0xff) {
252 ERROR("USB: downstream hub has no root-controller association");
253 m_RootHub = nullptr;
254 m_RootPort = 0xff;
256 return;
257 }
258
259 m_RootHub = upstream->m_RootHub;
260 m_RootPort = connection.port;
261 m_RootPortGeneration = connection.generation;
262}
263
265 if (port >= ConnectionChangePortCount) {
266 suppression = ConnectionChangeSuppression();
267 ERROR(
268 "USB: root-port connection-change suppression was requested for "
269 "invalid port "
270 << Dec << port << Hex);
271 return false;
272 }
273
275 while (true) {
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);
282 }
283#endif
285 continue;
286 }
287 if (count == ConnectionChangeCountMask) {
288 suppression = ConnectionChangeSuppression();
289 ERROR(
290 "USB: root-port connection-change suppression count "
291 "overflowed on port "
292 << Dec << port << Hex);
293 return false;
294 }
295
296 if (state.compareAndSwap(observed, observed + 1)) {
297 suppression = ConnectionChangeSuppression(this, port);
298 return true;
299 }
300 }
301}
302
304 if (port >= ConnectionChangePortCount) {
305 return false;
306 }
307
309 while (true) {
310 const size_t observed = state;
311 if (!(observed & ConnectionChangeCountMask)) {
312 return false;
313 }
314
315#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
316 if (g_ConnectionChangePendingHook) {
317 g_ConnectionChangePendingHook(this, port, observed);
318 }
319#endif
320
321 // Even an already-pending observation performs the CAS. A final
322 // release which wins this boundary changes the state to zero, making
323 // us retry and process the new hardware event normally instead of
324 // acknowledging it behind an already-issued replay.
325 if (state.compareAndSwap(observed, observed | ConnectionChangePending)) {
326 return true;
327 }
328 }
329}
330
331void UsbHub::releaseConnectionChangeSuppression(size_t port) {
332 if (port >= ConnectionChangePortCount) {
333 ERROR("USB: invalid root-port connection-change suppression release");
334 return;
335 }
336
338 bool replay = false;
339 while (true) {
340 const size_t observed = state;
341 const size_t count = observed & ConnectionChangeCountMask;
342 if (!count) {
343 ERROR(
344 "USB: unbalanced root-port connection-change suppression "
345 "release on port "
346 << Dec << port << Hex);
347 return;
348 }
349
350 const bool finalRelease = count == 1;
351 // Pending with a zero count is a transient replay-in-progress state.
352 // A new enumeration scope cannot overtake publication of the change
353 // retained by the scope which is ending here.
354 const size_t replacement =
355 finalRelease ? ((observed & ConnectionChangePending) ? ConnectionChangePending : 0)
356 : observed - 1;
357 if (state.compareAndSwap(observed, replacement)) {
358 replay = finalRelease && (observed & ConnectionChangePending);
359 break;
360 }
361 }
362
363 if (replay) {
365 const bool published = state.compareAndSwap(ConnectionChangePending, 0);
366 assert(published);
367 (void)published;
368 }
369}
370
372 ERROR(
373 "USB: root-port change replay has no controller implementation for "
374 "port "
375 << Dec << port << Hex);
376}
377
378#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
379void UsbHub::setConnectionChangePendingHookForTest(ConnectionChangePendingHook hook) {
380 g_ConnectionChangePendingHook = hook;
381}
382
383void UsbHub::setConnectionChangeReplayWaitHookForTest(ConnectionChangeReplayWaitHook hook) {
384 g_ConnectionChangeReplayWaitHook = hook;
385}
386#endif
387
388bool UsbHub::deviceConnected(uint8_t nPort, UsbSpeed speed) {
389 StartupActivity activity(this);
390 if (!activity)
391 return false;
392 const bool connected = deviceConnectedAdmitted(nPort, speed);
393 if (!connected)
394 activity.failed();
395 return connected;
396}
397
398bool UsbHub::deviceConnectedAdmitted(uint8_t nPort, UsbSpeed speed) {
399 NOTICE("USB: Adding device on port " << Dec << nPort << Hex);
400
401 UsbHub* pRootHub = m_RootHub;
402 if (!pRootHub) {
403 ERROR("USB: cannot enumerate a hub without a root controller");
404 return false;
405 }
406
407 // Only an HCD invocation names a root port. A downstream hub's local port
408 // number must never suppress an unrelated port on the root controller.
409 ConnectionChangeSuppression changeSuppression;
410 if (m_IsRootHub && !suppressConnectionChanges(nPort, changeSuppression)) {
411 return false;
412 }
413
414 struct PendingProbe {
415 explicit PendingProbe(UsbDeviceContainer* container) : container(container), probe() {}
416
417 UsbDeviceContainer* container;
419 };
420
421 List<PendingProbe*> pendingProbes;
422 {
423 LockGuard<Mutex> topologyGuard(m_TopologyLock);
424
425 // A repeated connected observation replaces the prior port generation.
426 // The per-hub topology lock keeps another observation from publishing a
427 // successor between retirement and generic-container publication.
428 bool retiredAddresses[128] = {};
429 bool pinnedSubtreeAddresses[128] = {};
430 retirePortContainersLocked(nPort, retiredAddresses, pinnedSubtreeAddresses);
431
432 {
433 // Every device shares address zero until SET_ADDRESS completes. Keeping
434 // this root-owned lock through generic-container publication also
435 // prevents an address from being reused before its tree owner is visible.
436 LockGuard<Mutex> enumerationGuard(pRootHub->m_EnumerationLock);
437 releaseRetiredAddressesLocked(nPort, retiredAddresses);
438 if (!portReset(nPort)) {
439 // Subtree pins keep every old address reserved because a descendant may
440 // still answer. Controller teardown is the safe release point.
441 NOTICE("USB: Port reset failed before enumeration (port " << nPort << ")");
442 return false;
443 }
444 pRootHub->releaseAddressesLocked(pinnedSubtreeAddresses);
445 if (!portSpeed(nPort, speed)) {
446 WARNING("USB: couldn't determine reset port speed (port " << nPort << ")");
447 return false;
448 }
449
450 size_t nRetry = 0;
451 uint8_t nAddress = 0;
452 UsbDevice* pDevice = nullptr;
453
454 // Try twice, releasing every failed attempt before the next allocation.
455 while (nRetry < 2) {
456 nAddress = pRootHub->m_UsedAddresses.getFirstClear();
457 if (nAddress > 127) {
458 ERROR("USB: HUB: Out of addresses!");
459 return false;
460 }
461
462 pRootHub->m_UsedAddresses.set(nAddress);
463 assert(!pRootHub->m_AddressReferences[nAddress]);
464 pRootHub->m_AddressReferences[nAddress] = 1;
465 NOTICE("USB: Allocated device on port " << Dec << nPort << Hex << " address " << nAddress);
466
467 pDevice = new UsbDevice(this, nPort, speed);
468 pDevice->initialise(nAddress);
469
470 if (pDevice->getUsbState() != UsbDevice::Configured) {
471 NOTICE(
472 "USB: Device initialisation ended up not giving a configured "
473 "device [retry "
474 << nRetry << " of 2].");
475
476 delete pDevice;
477 pDevice = nullptr;
478
479 NOTICE("USB: Performing a port reset on port " << nPort);
480 if (!portReset(nPort, true)) {
481 // SET_ADDRESS can take effect even when its completion was lost. If
482 // reset cannot prove the device returned to address zero, retaining
483 // this reservation is safer than assigning it to another device.
484 NOTICE("USB: Port reset failed (port " << nPort << ")");
485 return false;
486 }
487 pRootHub->releaseAddressLocked(nAddress);
488 } else {
489 NOTICE("USB: Device on port " << Dec << nPort << Hex << " accepted address " << nAddress);
490 break;
491 }
492
493 nRetry++;
494 }
495
496 if (nRetry == 2) {
497 NOTICE("Device initialisation couldn't configure the device.");
498 return false;
499 }
500
501 UsbDevice::DeviceDescriptor* pDescriptor = pDevice->getDescriptor();
502 Vector<UsbDevice::Interface*> interfaceList = pDevice->getConfiguration()->interfaceList;
503 for (size_t i = 0; i < interfaceList.count(); i++) {
504 UsbDevice::Interface* pInterface = interfaceList[i];
505 if (pInterface->nAlternateSetting)
506 continue;
507
508 UsbDevice* pInterfaceDevice = new UsbDevice(pDevice);
509 pInterfaceDevice->useInterface(i);
510
511 UsbDeviceContainer* pContainer = new UsbDeviceContainer(pInterfaceDevice);
512 auto* pending = new PendingProbe(pContainer);
513 if (!pContainer->tryAcquireProbe(pending->probe))
514 panic("USB: new device container rejected its initial probe");
515 {
516 Device::TreeLockGuard treeGuard;
517 pContainer->setParent(this);
518 addChild(pContainer);
519 }
520 pendingProbes.pushBack(pending);
521
522 NOTICE("USB: Device (address " << nAddress << "): " << pDescriptor->sVendor << " "
523 << pDescriptor->sProduct << ", class " << Dec
524 << pInterface->nClass << ":" << pInterface->nSubclass << ":"
525 << pInterface->nProtocol << Hex);
526 }
527
528 if (!pendingProbes.count()) {
529 delete pDevice;
530 pDevice = nullptr;
531 WARNING("USB: configured device exposed no default interface");
532 if (portReset(nPort, true))
533 pRootHub->releaseAddressLocked(nAddress);
534 return false;
535 }
536 delete pDevice;
537 }
538 }
539
540 changeSuppression.reset();
541
542 while (pendingProbes.count()) {
543 PendingProbe* pending = pendingProbes.popFront();
544 UsbPnP::instance().probeDeviceAdmitted(pending->container, pending->probe);
545 delete pending;
546 }
547 return true;
548}
549
550void UsbHub::deviceDisconnected(uint8_t nPort) {
551 LockGuard<Mutex> topologyGuard(m_TopologyLock);
552 deviceDisconnectedLocked(nPort);
553}
554
555void UsbHub::deviceDisconnectedLocked(uint8_t nPort) {
556 bool retiredAddresses[128] = {};
557 bool pinnedSubtreeAddresses[128] = {};
558 retirePortContainersLocked(nPort, retiredAddresses,
559 m_RetainDisconnectedAddresses ? pinnedSubtreeAddresses : nullptr);
560
561 UsbHub* addressOwner = m_RootHub ? m_RootHub : this;
562 LockGuard<Mutex> enumerationGuard(addressOwner->m_EnumerationLock);
563 releaseRetiredAddressesLocked(nPort, retiredAddresses);
564 // Driver-only hub unbind leaves physical downstream ports configured. The
565 // subtree pins therefore remain until the root HCD itself is destroyed.
566}
567
568void UsbHub::retirePortContainersLocked(uint8_t nPort, bool* retiredAddresses,
569 bool* pinnedSubtreeAddresses) {
570 List<UsbDeviceContainer*> retiredContainers;
571 {
572 Device::TreeLockGuard treeGuard;
573 for (size_t i = 0; i < m_Children.count();) {
574 Device* child = m_Children[i];
575 if (!child || child->getType() != Device::UsbContainer) {
576 ++i;
577 continue;
578 }
579
580 auto* container = static_cast<UsbDeviceContainer*>(child);
581 UsbDevice* pDevice = container->getUsbDevice();
582 if (!pDevice || pDevice->getPort() != nPort) {
583 ++i;
584 continue;
585 }
586
587 const uint8_t address = pDevice->getAddress();
588 if (address && address < 128)
589 retiredAddresses[address] = true;
590
591 container->closeProbeAdmission();
592 removeChild(i);
593 child->setParent(nullptr);
594 retiredContainers.pushBack(container);
595 }
596 }
597
598 for (List<UsbDeviceContainer*>::Iterator it = retiredContainers.begin();
599 it != retiredContainers.end(); ++it) {
600 drainSubtreeProbeAdmissions(*it);
601 }
602
603 if (pinnedSubtreeAddresses) {
604 UsbHub* addressOwner = m_RootHub ? m_RootHub : this;
605 LockGuard<Mutex> enumerationGuard(addressOwner->m_EnumerationLock);
606 Device::TreeLockGuard treeGuard;
607 for (List<UsbDeviceContainer*>::Iterator it = retiredContainers.begin();
608 it != retiredContainers.end(); ++it) {
609 collectUsbAddresses(*it, pinnedSubtreeAddresses);
610 }
611 addressOwner->retainAddressesLocked(pinnedSubtreeAddresses);
612 }
613
614 while (retiredContainers.count()) {
615 UsbDeviceContainer* container = retiredContainers.popFront();
616 ServiceManager& services = ServiceManager::instance();
617 const ServiceFeatures::Type operation =
619 // mountroot is runtime-pinned; absence is valid before filesystem startup.
620 if (services.getService(String("mountroot")) &&
621 !services.serve(String("mountroot"), operation, static_cast<Device*>(container),
622 sizeof(Device*))) {
623 panic("USB: couldn't withdraw filesystems before device retirement");
624 }
626 prepareSubtreeForDisconnection(container);
627 }
628 delete container;
629 }
630}
631
632void UsbHub::collectUsbAddresses(Device* device, bool* addresses) {
633 if (!device || !addresses)
634 return;
635
636 if (device->getType() == Device::UsbContainer) {
637 auto* container = static_cast<UsbDeviceContainer*>(device);
638 UsbDevice* usbDevice = container->getUsbDevice();
639 if (usbDevice) {
640 const uint8_t address = usbDevice->getAddress();
641 if (address && address < 128)
642 addresses[address] = true;
643 }
644 }
645
646 for (size_t i = 0; i < device->getNumChildren(); ++i)
647 collectUsbAddresses(device->getChild(i), addresses);
648}
649
650void UsbHub::collectImmediateUsbContainers(Device* device, List<UsbDeviceContainer*>& containers) {
651 if (!device)
652 return;
653
654 for (size_t i = 0; i < device->getNumChildren(); ++i) {
655 Device* child = device->getChild(i);
656 if (child->getType() == Device::UsbContainer) {
657 containers.pushBack(static_cast<UsbDeviceContainer*>(child));
658 } else {
659 collectImmediateUsbContainers(child, containers);
660 }
661 }
662}
663
664void UsbHub::drainSubtreeProbeAdmissions(UsbDeviceContainer* container) {
665 if (!container)
666 return;
667
668 // A container's probe can replace its complete device subtree. Drain the
669 // parent before retaining child pointers so replacement cannot free a
670 // descendant while teardown is waiting on its admission barrier.
671 container->closeProbeAdmission();
672 container->waitForProbes();
673 UsbDevice* device = container->getUsbDevice();
674 if (device) {
675 device->quiesceForRetirement();
676 }
677
679 {
680 Device::TreeLockGuard treeGuard;
681 collectImmediateUsbContainers(container, children);
682 }
683 for (List<UsbDeviceContainer*>::Iterator it = children.begin(); it != children.end(); ++it)
684 drainSubtreeProbeAdmissions(*it);
685}
686
687void UsbHub::prepareSubtreeForDisconnection(UsbDeviceContainer* container) {
688 UsbDevice* device = container->getUsbDevice();
689 if (device) {
690 device->prepareForDisconnection();
691 }
693 {
694 Device::TreeLockGuard treeGuard;
695 collectImmediateUsbContainers(container, children);
696 }
697 for (List<UsbDeviceContainer*>::Iterator it = children.begin(); it != children.end(); ++it) {
698 prepareSubtreeForDisconnection(*it);
699 }
700}
701
702void UsbHub::retainAddressLocked(size_t address) {
703 assert(address && address < 128);
704 assert(m_UsedAddresses.test(address));
705 assert(m_AddressReferences[address]);
706 ++m_AddressReferences[address];
707}
708
709void UsbHub::releaseAddressLocked(size_t address) {
710 assert(address && address < 128);
711 assert(m_UsedAddresses.test(address));
712 assert(m_AddressReferences[address]);
713 if (!--m_AddressReferences[address]) {
714 releaseDeviceAddress(address);
715 m_UsedAddresses.clear(address);
716 }
717}
718
719void UsbHub::retainAddressesLocked(const bool* addresses) {
720 for (size_t address = 1; address < 128; ++address) {
721 if (addresses[address])
722 retainAddressLocked(address);
723 }
724}
725
726void UsbHub::releaseAddressesLocked(const bool* addresses) {
727 for (size_t address = 1; address < 128; ++address) {
728 if (addresses[address])
729 releaseAddressLocked(address);
730 }
731}
732
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])
737 continue;
738 retiredAny = true;
739 if (m_RootHub) {
740 m_RootHub->releaseAddressLocked(address);
741 NOTICE("USB: Retired device on port " << Dec << nPort << " address " << address << Hex);
742 }
743 }
744 if (retiredAny && !m_RootHub)
745 ERROR("USB: cannot release an address without a root controller");
746}
747
748void UsbHub::syncCallback(uintptr_t pParam, ssize_t nResult) {
749 if (!pParam)
750 return;
751 SyncParam* pSyncParam = reinterpret_cast<SyncParam*>(pParam);
752 pSyncParam->nResult = nResult;
753 pSyncParam->semaphore.release();
754 pSyncParam->releaseOwner();
755}
756
757UsbHub::SyncParam::~SyncParam() {
758#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
759 g_HostedSyncParamDestructions += 1;
760#endif
761}
762
763void UsbHub::SyncParam::releaseOwner() {
764 if ((owners -= 1) == 0) {
765 delete this;
766 }
767}
768
769ssize_t UsbHub::doSync(uintptr_t nTransaction, uint32_t timeout) {
770 // Terminal cancellation must return through this scope so the caller's
771 // asynchronous-transaction reference is always released.
772 TerminationDeferral transactionLifetime;
773
774 // Create a structure to hold the semaphore and the result
775 SyncParam* pSyncParam = new SyncParam();
776
777 // Send the async request. A rejected request has no callback owner.
778 const uintptr_t callbackParam = reinterpret_cast<uintptr_t>(pSyncParam);
779 if (!doAsync(nTransaction, syncCallback, callbackParam)) {
780 pSyncParam->releaseOwner();
781 pSyncParam->releaseOwner();
782 return -TransactionError;
783 }
784 // The caller and callback own one reference each. A timeout relinquishes
785 // only the caller's reference; controllers must still complete every
786 // accepted asynchronous transaction exactly once.
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;
791
792#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
793 if (bTimeout && g_HostedSyncTimeoutHook) {
794 g_HostedSyncTimeoutHook();
795 }
796#endif
797
798 if (!completed) {
799 // Cancellation is also the hardware ownership barrier. It either
800 // completes this callback itself or waits for a completion which won
801 // the race, so no caller-owned transfer buffer can outlive this scope.
802 cancelAsyncAndDrain(nTransaction, syncCallback, callbackParam);
803 const bool callbackDrained = pSyncParam->semaphore.acquireForCompletion();
804 (void)callbackDrained;
805 }
806
807 const ssize_t result = completed ? pSyncParam->nResult : -TransactionError;
808 if (bTimeout) {
809 WARNING("USB: a transaction timed out.");
810 }
811 pSyncParam->releaseOwner();
812 return result;
813}
814
815#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
816namespace {
817struct HostedSyncCallback {
818 HostedSyncCallback()
819 : callback(nullptr),
820 parameter(0),
821 start(0),
822 finished(0),
823 thread(nullptr),
824 completionState(0),
825 callbackCount(0),
826 bufferOwned(false) {}
827
828 void (*callback)(uintptr_t, ssize_t);
829 uintptr_t parameter;
830 Semaphore start;
831 Semaphore finished;
832 Thread* thread;
833 Atomic<size_t> completionState;
834 Atomic<size_t> callbackCount;
835 Atomic<bool> bufferOwned;
836};
837
838int hostedSyncCallbackThread(void* parameter) {
839 HostedSyncCallback* context = reinterpret_cast<HostedSyncCallback*>(parameter);
840 const bool started = context->start.acquireForCompletion();
841 (void)started;
842 if (context->completionState.compareAndSwap(1, 2)) {
843 context->bufferOwned = false;
844 context->callbackCount += 1;
845 context->callback(context->parameter, 0x55);
846 }
847 context->finished.release();
848 return 0;
849}
850
851class HostedSyncHub : public UsbHub {
852 public:
853 explicit HostedSyncHub(bool accept = true) : UsbHub(), m_Callback(), m_Accept(accept) {}
854
855 ~HostedSyncHub() override {
856 if (m_Callback.thread) {
857 m_Callback.start.release();
858 m_Callback.thread->joinForCompletion();
859 m_Callback.thread = nullptr;
860 }
861 }
862
863 void addTransferToTransaction(uintptr_t, bool, UsbPid, uintptr_t, size_t) override {}
864
865 uintptr_t createTransaction(UsbEndpoint) override {
866 return 1;
867 }
868
869 bool doAsync(uintptr_t, void (*callback)(uintptr_t, ssize_t), uintptr_t parameter) override {
870 if (!m_Accept)
871 return false;
872
873 m_Callback.callback = callback;
874 m_Callback.parameter = parameter;
875 m_Callback.completionState = 1;
876 m_Callback.bufferOwned = true;
877 m_Callback.thread = new Thread(Scheduler::instance().getKernelProcess(),
878 hostedSyncCallbackThread, &m_Callback, nullptr, false, true);
879 m_Callback.thread->setName("hosted USB timeout callback");
880 return true;
881 }
882
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)) {
887 // This transition is the fake controller's deterministic DMA
888 // release boundary.
889 m_Callback.bufferOwned = false;
890 m_Callback.callbackCount += 1;
891 callback(parameter, -TransactionError);
892 }
893
894 if (m_Callback.thread) {
895 m_Callback.start.release();
896 const bool finished = m_Callback.finished.acquireForCompletion();
897 (void)finished;
898 m_Callback.thread->joinForCompletion();
899 m_Callback.thread = nullptr;
900 }
901 }
902
903 bool addInterruptInHandler(UsbEndpoint, uintptr_t, uint16_t, void (*)(uintptr_t, ssize_t),
904 UsbInterruptInHandle&, uintptr_t) override {
905 return false;
906 }
907
908 bool portReset(uint8_t, bool) override {
909 return true;
910 }
911
912 void completeInsideTimeoutHandoff() {
913 m_Callback.start.release();
914 const bool finished = m_Callback.finished.acquireForCompletion();
915 (void)finished;
916 m_Callback.thread->joinForCompletion();
917 m_Callback.thread = nullptr;
918 }
919
920 bool completedExactlyOnce() const {
921 return static_cast<size_t>(m_Callback.callbackCount) == 1 &&
922 !static_cast<bool>(m_Callback.bufferOwned);
923 }
924
925 private:
926 bool cancelInterruptInAndDrain(const UsbInterruptInToken&, void (*)(uintptr_t, ssize_t),
927 uintptr_t, bool) override {
928 panic("hosted synchronous USB hub unexpectedly owned an interrupt subscription");
929 return false;
930 }
931
932 HostedSyncCallback m_Callback;
933 bool m_Accept;
934};
935
936HostedSyncHub* g_pHostedSyncHub = nullptr;
937
938void completeHostedSyncTimeout() {
939 g_pHostedSyncHub->completeInsideTimeoutHandoff();
940}
941} // namespace
942
943bool UsbHub::runHostedSyncOwnershipRegression() {
944 const size_t destructionsBefore = g_HostedSyncParamDestructions;
945 bool rejectionPassed = false;
946 {
947 HostedSyncHub hub(false);
948 rejectionPassed = hub.doSync(1, 1) == -TransactionError;
949 }
950 if (rejectionPassed) {
951 NOTICE("HOSTED-WAIT-TEST: PASS usb-sync-rejected-no-callback");
952 } else {
953 ERROR(
954 "HOSTED-WAIT-TEST: FAIL usb-sync-rejected-no-callback: rejected "
955 "submission retained a callback obligation");
956 }
957
958 bool cancelPassed = false;
959 {
960 HostedSyncHub hub;
961 const ssize_t result = hub.doSync(1, 1);
962 cancelPassed = result == -TransactionError && hub.completedExactlyOnce();
963 }
964 if (cancelPassed) {
965 NOTICE("HOSTED-WAIT-TEST: PASS usb-sync-timeout-cancel-drain");
966 } else {
967 ERROR(
968 "HOSTED-WAIT-TEST: FAIL usb-sync-timeout-cancel-drain: doSync "
969 "returned before cancellation released the fake DMA buffer");
970 }
971
972 bool handoffPassed = false;
973 {
974 HostedSyncHub hub;
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();
981 }
982 if (handoffPassed) {
983 NOTICE("HOSTED-WAIT-TEST: PASS usb-sync-timeout-completion-handoff");
984 } else {
985 ERROR(
986 "HOSTED-WAIT-TEST: FAIL usb-sync-timeout-completion-handoff: "
987 "completion/cancellation race delivered more than once");
988 }
989
990 return rejectionPassed && cancelPassed && handoffPassed &&
991 g_HostedSyncParamDestructions == (destructionsBefore + 3);
992}
993
994EXPORTED_PUBLIC bool runHostedUsbSyncOwnershipRegression() {
995 return UsbHub::runHostedSyncOwnershipRegression();
996}
997#endif
998
999#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
1000namespace {
1001constexpr bool PinInterruptTestThreads = HOSTED;
1002
1003void* interruptRegressionRunner() {
1004 ProcessorInformation& information = Processor::information();
1005 Thread* thread = information.getCurrentThread();
1006 return thread ? static_cast<void*>(thread) : static_cast<void*>(&information);
1007}
1008
1009class InterruptOwnershipHub : public UsbHub {
1010 public:
1011 InterruptOwnershipHub()
1012 : UsbHub(),
1013 m_Callbacks(),
1014 m_Callback(nullptr),
1015 m_Parameter(0),
1016 m_CallbackRunner(nullptr),
1017 m_CancelCalls(0) {}
1018
1019 ~InterruptOwnershipHub() override {
1020 if (m_Callbacks.isOpen())
1021 m_Callbacks.closeAndWait();
1022 }
1023
1024 void addTransferToTransaction(uintptr_t, bool, UsbPid, uintptr_t, size_t) override {}
1025
1026 uintptr_t createTransaction(UsbEndpoint) override {
1027 return static_cast<uintptr_t>(-1);
1028 }
1029
1030 bool doAsync(uintptr_t, void (*)(uintptr_t, ssize_t), uintptr_t) override {
1031 return false;
1032 }
1033
1034 void cancelAsyncAndDrain(uintptr_t, void (*)(uintptr_t, ssize_t), uintptr_t) override {
1035 panic("interrupt ownership regression unexpectedly cancelled an async transfer");
1036 }
1037
1038 bool addInterruptInHandler(UsbEndpoint, uintptr_t, uint16_t, void (*callback)(uintptr_t, ssize_t),
1039 UsbInterruptInHandle& handle, uintptr_t parameter) override {
1040 if (m_Callback || !callback || handle || !m_Callbacks.isOpen())
1041 return false;
1042
1043 const UsbInterruptInToken token = {0x494e5452, 1};
1044 if (!publishInterruptInHandle(handle, token, callback, parameter))
1045 return false;
1046
1047 m_Callback = callback;
1048 m_Parameter = parameter;
1049 return true;
1050 }
1051
1052 bool portReset(uint8_t, bool) override {
1053 return true;
1054 }
1055
1056 bool fire() {
1057 OperationBarrier::Lease callbackLease;
1058 if (!m_Callback || !m_Callbacks.tryAcquire(callbackLease))
1059 return false;
1060
1061 m_CallbackRunner = interruptRegressionRunner();
1062 m_Callback(m_Parameter, 1);
1063 m_CallbackRunner = nullptr;
1064 return true;
1065 }
1066
1067 size_t cancelCalls() const {
1068 return m_CancelCalls;
1069 }
1070
1071 private:
1072 bool inInterruptCallbackContext() const override {
1073 return m_CallbackRunner == interruptRegressionRunner();
1074 }
1075
1076 bool cancelInterruptInAndDrain(const UsbInterruptInToken& token,
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");
1082 }
1083
1084 if (!producerAlreadyStopped) {
1085 m_CancelCalls += 1;
1086 m_Callbacks.close();
1087 }
1088
1089 if (m_CallbackRunner == interruptRegressionRunner())
1090 return false;
1091
1092 m_Callbacks.wait();
1093 return true;
1094 }
1095
1096 OperationBarrier m_Callbacks;
1097 void (*m_Callback)(uintptr_t, ssize_t);
1098 uintptr_t m_Parameter;
1099 Atomic<void*> m_CallbackRunner;
1100 size_t m_CancelCalls;
1101};
1102
1103class ProbeLeafUsbDevice : public UsbDevice {
1104 public:
1105 ProbeLeafUsbDevice(UsbHub* hub, uint8_t port, uint8_t address) : UsbDevice(hub, port, FullSpeed) {
1106 m_nAddress = address;
1107 }
1108};
1109
1110class ProbeSubtreeUsbDevice : public Device, public UsbDevice {
1111 public:
1112 ProbeSubtreeUsbDevice(UsbHub* hub, uint8_t port, uint8_t address)
1113 : Device(), UsbDevice(hub, port, FullSpeed) {
1114 m_nAddress = address;
1115 }
1116
1117 explicit ProbeSubtreeUsbDevice(UsbDevice* device) : Device(), UsbDevice(device) {}
1118
1119 bool hasSubtree() const override {
1120 return true;
1121 }
1122
1123 Device* getDevice() override {
1124 return this;
1125 }
1126};
1127
1128struct DescendantProbePublication {
1129 explicit DescendantProbePublication(UsbDeviceContainer* container)
1130 : container(container), probe(), entered(0), publish(0), published(0) {}
1131
1132 UsbDeviceContainer* container;
1134 Semaphore entered;
1135 Semaphore publish;
1136 Atomic<size_t> published;
1137};
1138
1139int publishDescendantProbe(void* parameter) {
1140 auto* context = reinterpret_cast<DescendantProbePublication*>(parameter);
1141 context->entered.release();
1142 const bool released = context->publish.acquireForCompletion();
1143 (void)released;
1144
1145 UsbDevice* generic = context->container->getUsbDevice();
1146 auto* replacement = new ProbeSubtreeUsbDevice(generic);
1147 auto* grandchild = new UsbDeviceContainer(new ProbeLeafUsbDevice(nullptr, 3, 7));
1148 {
1149 Device::TreeLockGuard treeGuard;
1150 grandchild->setParent(replacement);
1151 replacement->addChild(grandchild);
1152 }
1153 if (context->container->replaceUsbDevice(replacement))
1154 context->published += 1;
1155 context->probe = OperationBarrier::Lease();
1156 return 0;
1157}
1158
1159struct ProbeSubtreeRetirement {
1160 ProbeSubtreeRetirement(UsbHub* hub, uint8_t port)
1161 : hub(hub), port(port), started(0), finished(0), retired{}, pinned{} {}
1162
1163 UsbHub* hub;
1164 uint8_t port;
1165 Atomic<size_t> started;
1166 Atomic<size_t> finished;
1167 bool retired[128];
1168 bool pinned[128];
1169};
1170
1171struct ExternalInterruptCancellation {
1172 ExternalInterruptCancellation(InterruptOwnershipHub* hub, UsbInterruptInHandle* handle)
1173 : hub(hub),
1174 handle(handle),
1175 callbackEntered(0),
1176 releaseCallback(0),
1177 callbackCalls(0),
1178 fireReturned(0),
1179 resetStarted(0),
1180 resetReturned(0),
1181 resetSucceeded(0),
1182 callbackResetRejected(0),
1183 callbackProcessor(static_cast<size_t>(-1)),
1184 resetProcessor(static_cast<size_t>(-1)) {}
1185
1186 InterruptOwnershipHub* hub;
1187 UsbInterruptInHandle* handle;
1188 Semaphore callbackEntered;
1189 Semaphore releaseCallback;
1190 Atomic<size_t> callbackCalls;
1191 Atomic<size_t> fireReturned;
1192 Atomic<size_t> resetStarted;
1193 Atomic<size_t> resetReturned;
1194 Atomic<size_t> resetSucceeded;
1195 Atomic<size_t> callbackResetRejected;
1196 Atomic<size_t> callbackProcessor;
1197 Atomic<size_t> resetProcessor;
1198};
1199
1200void blockingInterruptCallback(uintptr_t parameter, ssize_t) {
1201 auto* context = reinterpret_cast<ExternalInterruptCancellation*>(parameter);
1202 context->callbackProcessor = Processor::id();
1203 context->callbackCalls += 1;
1204 context->callbackEntered.release();
1205 const bool released = context->releaseCallback.acquireForCompletion();
1206 (void)released;
1207 if (!context->handle->reset())
1208 context->callbackResetRejected += 1;
1209}
1210
1211int fireInterruptCallback(void* parameter) {
1212 auto* context = reinterpret_cast<ExternalInterruptCancellation*>(parameter);
1213 if (context->hub->fire())
1214 context->fireReturned += 1;
1215 return 0;
1216}
1217
1218int resetInterruptHandle(void* parameter) {
1219 auto* context = reinterpret_cast<ExternalInterruptCancellation*>(parameter);
1220 context->resetProcessor = Processor::id();
1221 context->resetStarted += 1;
1222 if (context->handle->reset())
1223 context->resetSucceeded += 1;
1224 context->resetReturned += 1;
1225 return 0;
1226}
1227
1228struct SelfInterruptCancellation {
1229 explicit SelfInterruptCancellation(UsbInterruptInHandle* handle)
1230 : handle(handle), callbacks(0), resetRejected(0), ownershipRetained(0) {}
1231
1232 UsbInterruptInHandle* handle;
1233 Atomic<size_t> callbacks;
1234 Atomic<size_t> resetRejected;
1235 Atomic<size_t> ownershipRetained;
1236};
1237
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;
1245}
1246
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;
1252}
1253
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;
1259}
1260} // namespace
1261
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;
1267 return 0;
1268}
1269
1270bool UsbHub::runInterruptOwnershipRegression() {
1271 bool subtreeAddressPinPassed = false;
1272 {
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;
1280
1281 bool subtreeAddresses[128] = {};
1282 subtreeAddresses[ParentAddress] = true;
1283 subtreeAddresses[DescendantAddress] = true;
1284 hub.retainAddressesLocked(subtreeAddresses);
1285
1286 // Recursive driver teardown releases both live owners, but the repeated
1287 // connection's subtree pins must keep both addresses unavailable until
1288 // the parent reset establishes a new generation boundary.
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);
1296 }
1297
1298 bool driverUnbindPinPassed = false;
1299 {
1300 InterruptOwnershipHub hub;
1301 constexpr size_t DescendantAddress = 7;
1302 hub.m_UsedAddresses.set(DescendantAddress);
1303 hub.m_AddressReferences[DescendantAddress] = 1;
1304
1305 bool descendantAddresses[128] = {};
1306 descendantAddresses[DescendantAddress] = true;
1307 hub.retainAddressesLocked(descendantAddresses);
1308 hub.releaseAddressLocked(DescendantAddress);
1309
1310 // Unbinding a hub driver does not reset physical downstream ports, so the
1311 // retained reference must deliberately survive the software subtree.
1312 driverUnbindPinPassed = hub.m_UsedAddresses.test(DescendantAddress) &&
1313 hub.m_AddressReferences[DescendantAddress] == 1;
1314 }
1315
1316 bool subtreeProbeFixedPointPassed = false;
1317 {
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;
1326 }
1327
1328 auto* parentDevice = new ProbeSubtreeUsbDevice(&hub, 1, ParentAddress);
1329 auto* parentContainer = new UsbDeviceContainer(parentDevice);
1330 auto* descendantDevice = new ProbeSubtreeUsbDevice(&hub, 2, DescendantAddress);
1331 auto* descendantContainer = new UsbDeviceContainer(descendantDevice);
1332 auto* replacedGrandchild =
1333 new UsbDeviceContainer(new ProbeLeafUsbDevice(&hub, 4, ReplacedGrandchildAddress));
1334 {
1335 Device::TreeLockGuard treeGuard;
1336 replacedGrandchild->setParent(descendantDevice);
1337 descendantDevice->addChild(replacedGrandchild);
1338 descendantContainer->setParent(parentDevice);
1339 parentDevice->addChild(descendantContainer);
1340 parentContainer->setParent(&hub);
1341 hub.addChild(parentContainer);
1342 }
1343
1344 DescendantProbePublication publication(descendantContainer);
1345 const bool probeAdmitted = descendantContainer->tryAcquireProbe(publication.probe);
1346 Process* process = Scheduler::instance().getKernelProcess();
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();
1357 }
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);
1363 retirementBlocked =
1364 retirementStarted && waitForInterruptDrain(retiree) && !retirement.finished;
1365 }
1366
1367 publication.publish.release();
1368 const bool publisherJoined = !publisher || publisher->joinForCompletion();
1369 const bool retireeJoined = !retiree || retiree->joinForCompletion();
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;
1377
1378 hub.releaseAddressesLocked(retirement.pinned);
1379 for (size_t address = ParentAddress; address <= ReplacedGrandchildAddress; ++address)
1380 hub.releaseAddressLocked(address);
1381 }
1382
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;
1388#else
1389 constexpr size_t MaxExternalAttempts = 1;
1390#endif
1391 size_t externalAttempts = 0;
1392 while (externalAttempts < MaxExternalAttempts && !externalPassed) {
1393 ++externalAttempts;
1394 InterruptOwnershipHub hub;
1395 UsbInterruptInHandle handle;
1396 ExternalInterruptCancellation context(&hub, &handle);
1397 UsbEndpoint endpoint(1, 1, 1, FullSpeed, 8);
1398 const bool submitted = hub.addInterruptInHandler(endpoint, 0x1000, 8, blockingInterruptCallback,
1399 handle, reinterpret_cast<uintptr_t>(&context));
1400
1401 Process* process = Scheduler::instance().getKernelProcess();
1402 Thread* delivery = nullptr;
1403 Thread* cancellation = nullptr;
1404 bool callbackEntered = false;
1405 bool resetStarted = false;
1406 bool resetBlocked = false;
1407 if (submitted) {
1408 delivery = new Thread(process, fireInterruptCallback, &context, nullptr, false,
1409 PinInterruptTestThreads);
1410 delivery->setName("USB interrupt ownership callback");
1411 callbackEntered = context.callbackEntered.acquireForCompletion();
1412 }
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;
1419 }
1420
1421 context.releaseCallback.release();
1422 const bool deliveryJoined = !delivery || delivery->joinForCompletion();
1423 const bool cancellationJoined = !cancellation || cancellation->joinForCompletion();
1424 bool cleanupSucceeded = true;
1425 if (handle)
1426 cleanupSucceeded = handle.reset();
1427
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)
1439 break;
1440#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1441 externalPassed = callbackProcessor != resetProcessor;
1442#else
1443 externalPassed = true;
1444#endif
1445 }
1446
1447 bool selfPassed = false;
1448 {
1449 InterruptOwnershipHub hub;
1450 UsbInterruptInHandle handle;
1451 SelfInterruptCancellation context(&handle);
1452 UsbEndpoint endpoint(1, 1, 1, FullSpeed, 8);
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();
1463 }
1464
1465#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1466 NOTICE("QEMU-CONCURRENCY-TEST: usb-interrupt-cancel cpus="
1467 << Dec << callbackProcessor << "/" << resetProcessor << " attempts=" << externalAttempts);
1468#endif
1469 return subtreeAddressPinPassed && driverUnbindPinPassed && subtreeProbeFixedPointPassed &&
1470 externalPassed && selfPassed;
1471}
1472
1473#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1474bool UsbHub::runHostedInterruptOwnershipRegression() {
1475 const bool passed = runInterruptOwnershipRegression();
1476 if (passed) {
1477 NOTICE("HOSTED-WAIT-TEST: PASS usb-interrupt-handle-cancel-drain");
1478 } else {
1479 ERROR(
1480 "HOSTED-WAIT-TEST: FAIL usb-interrupt-handle-cancel-drain: "
1481 "subtree address pin, external drain, or callback-context retry failed");
1482 }
1483 return passed;
1484}
1485
1486EXPORTED_PUBLIC bool runHostedUsbInterruptOwnershipRegression() {
1487 return UsbHub::runHostedInterruptOwnershipRegression();
1488}
1489#endif
1490
1491#if PEDIGREE_CONCURRENCY_SMOKE_TESTS
1492bool UsbHub::runQemuInterruptOwnershipRegression() {
1493 const bool passed = runInterruptOwnershipRegression();
1494 if (passed) {
1495 NOTICE("QEMU-CONCURRENCY-TEST: PASS usb-interrupt-cancel-drain-smp");
1496 } else {
1497 ERROR(
1498 "QEMU-CONCURRENCY-TEST: FAIL usb-interrupt-cancel-drain-smp: "
1499 "subtree address pin, external drain, callback-context retry, or CPU spread failed");
1500 }
1501 return passed;
1502}
1503#endif
1504#endif
virtual Vector< Address * > & addresses()
Definition Device.h:266
@ UsbContainer
Definition Device.h:89
@ UsbController
Same as Controller, but exposes USB devices.
Definition Device.h:87
void addChild(Device *pDevice)
Definition Device.cc:135
Vector< Device * > m_Children
Definition Device.h:351
virtual Type getType()
Definition Device.h:178
void setParent(Device *p)
Definition Device.h:170
void removeChild(size_t n)
Definition Device.cc:147
bool test(size_t n) const
void clear(size_t n)
void set(size_t n)
Definition List.h:61
Iterator begin()
Definition List.h:122
::Iterator< T, node_t > Iterator
Definition List.h:67
Iterator end()
Definition List.h:132
static ProcessorId id()
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
void release(size_t n=1)
Definition Semaphore.cc:549
MUST_USE_RESULT bool acquireWithError(size_t n, size_t timeoutSecs, size_t timeoutUsecs, SemaphoreError &error)
Definition Semaphore.cc:360
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:372
Service * getService(const String &serviceName)
bool serve(const String &serviceName, ServiceFeatures::Type type, void *data, size_t dataLen)
bool joinForCompletion()
Definition Thread.cc:2750
Status getStatus() const
Definition Thread.h:433
UsbState getUsbState()
Returns the current state of the device.
Definition UsbDevice.h:211
ConfigDescriptor * getConfiguration()
Returns the configuration in use.
Definition UsbDevice.h:221
void useInterface(uint8_t nInterface)
Switches to the given interface.
Definition UsbDevice.cc:599
uint8_t getPort()
Returns the number of the port on which the device is connected.
Definition UsbDevice.h:197
uint8_t getAddress()
Returns the current address of the device.
Definition UsbDevice.h:192
DeviceDescriptor * getDescriptor()
Returns the device descriptor of the device.
Definition UsbDevice.h:216
void initialise(uint8_t nAddress)
Initialises the device at the given address.
Definition UsbDevice.cc:239
Mutex m_TopologyLock
Definition UsbHub.h:298
ssize_t doSync(uintptr_t nTransaction, uint32_t timeout=5000)
Definition UsbHub.cc:769
UsbHub * m_RootHub
Definition UsbHub.h:309
virtual void replaySuppressedConnectionChange(size_t port)
Definition UsbHub.cc:371
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)=0
Gets a UsbDevice from a given vendor:product pair.
void retainDisconnectedAddressesUntilControllerTeardown()
Definition UsbHub.cc:242
size_t m_RootPortGeneration
Definition UsbHub.h:318
MUST_USE_RESULT bool suppressConnectionChanges(size_t port, ConnectionChangeSuppression &suppression)
Definition UsbHub.cc:264
static void syncCallback(uintptr_t pParam, ssize_t ret)
Callback used by synchronous transactions.
Definition UsbHub.cc:748
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.
Definition UsbHub.cc:388
void deviceDisconnected(uint8_t nPort)
Called when a device is disconnected from a port on the hub.
Definition UsbHub.cc:550
virtual bool portSpeed(uint8_t, UsbSpeed &)
Definition UsbHub.h:325
virtual void releaseDeviceAddress(uint8_t)
Definition UsbHub.h:336
virtual size_t currentRootPortGeneration(size_t port) const
Definition UsbHub.h:383
MUST_USE_RESULT bool publishInterruptInHandle(UsbInterruptInHandle &handle, const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter)
Definition UsbHub.cc:207
Mutex m_EnumerationLock
Definition UsbHub.h:295
RootConnection rootConnectionForChild(uint8_t childPort) const
Definition UsbHub.cc:201
bool m_IsRootHub
Definition UsbHub.h:312
virtual Type getType()
Definition UsbHub.cc:246
size_t m_AddressReferences[128]
Definition UsbHub.h:292
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)
Definition UsbHub.cc:303
void attachToUpstreamHub(UsbHub *upstream, const RootConnection &connection)
Definition UsbHub.cc:250
uint8_t m_RootPort
Definition UsbHub.h:315
ExtensibleBitmap m_UsedAddresses
Definition UsbHub.h:289
Atomic< size_t > m_ConnectionChangeStates[ConnectionChangePortCount]
Definition UsbHub.h:306
bool m_RetainDisconnectedAddresses
Definition UsbHub.h:321
void disconnectAllDevices()
Definition UsbHub.cc:217
MUST_USE_RESULT bool reset()
Definition UsbHub.cc:99
static UsbPnP & instance()
Singleton design.
Definition UsbPnP.h:95
A vector / dynamic array.
Definition Vector.h:33
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
T popFront()
Definition List.h:330
size_t count() const
Definition List.h:212
void pushBack(const T &value)
Definition List.h:216
size_t count() const
Definition Vector.h:270
Structure used synchronous transactions.
Definition UsbHub.h:259