The Pedigree Project 0.1
XhciLifecycle.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/IrqManager.h"
5#include "pedigree/kernel/machine/Machine.h"
6#include "pedigree/kernel/machine/Pci.h"
7#include "pedigree/kernel/panic.h"
8#include "pedigree/kernel/process/TerminationDeferral.h"
9#include "pedigree/kernel/process/Thread.h"
10
11#include "Xhci.h"
12using namespace XhciHw;
13bool Xhci::halt() {
14 if (!m_HardwareOwned)
15 return true;
16 write(m_Runtime + 0x20, 1);
17 write(m_Op, read(m_Op) & ~5U);
18 (void)read(m_Op);
19 return wait(m_Op + 4, 1, 1, 1000);
20}
21void Xhci::failLocked() {
22 if (m_Failed)
23 return;
24 if (!halt())
25 panic("xHCI: DMA did not halt; refusing to release memory");
26 m_Online = false;
27 m_Stopping = true;
28 m_Failed = true;
29 ERROR("xHCI: controller halted after transport failure");
30 m_CommandDone.release();
31 for (auto* transaction : m_Transactions)
32 if (transaction && transaction->submitted && !transaction->captured)
33 finishLocked(*transaction, -TransactionError, false);
34}
35void Xhci::cancelTransfer(uintptr_t id, size_t generation, bool recurring,
36 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter) {
37 TerminationDeferral lifetime;
38 {
39 LockGuard<Mutex> cancellation(m_CancelLock);
40 LockGuard<Mutex> configuration(m_ConfigurationLock);
41 uint8_t slot = 0, dci = 0;
42 bool stop = false;
43 {
44 LockGuard<Mutex> lock(m_Lock);
45 if (id >= MaxTransactions || !m_Transactions[id])
46 return;
47 auto& transaction = *m_Transactions[id];
48 if (transaction.callback != callback || transaction.parameter != parameter ||
49 transaction.periodic != recurring || (recurring && transaction.generation != generation))
50 return;
51 generation = transaction.generation;
52 transaction.cancelling = true;
53 slot = transaction.slot;
54 dci = transaction.dci;
55 stop = transaction.submitted && m_Online;
56 }
57 if (stop && !resetEndpointHardware(slot, dci, true)) {
58 LockGuard<Mutex> lock(m_Lock);
59 failLocked();
60 }
61 {
62 LockGuard<Mutex> lock(m_Lock);
63 auto* transaction = m_Transactions[id];
64 if (transaction && transaction->generation == generation) {
65 if (recurring) {
66 if (transaction->submitted)
67 transaction->endpoint->ring.retire(transaction->trbCount);
68 transaction->endpoint->active = nullptr;
69 freeTransactionLocked(id);
70 m_Transfers.leave();
71 } else if (!transaction->captured)
72 finishLocked(*transaction, -TransactionError, false);
73 }
74 }
75 }
76 if (!recurring)
77 (void)m_Deliveries.drain({id, generation});
78}
79void Xhci::cancelAsyncAndDrain(uintptr_t id, void (*callback)(uintptr_t, ssize_t),
80 uintptr_t parameter) {
82 if (!m_Cancellations.tryAcquire(admission))
83 return;
84 cancelTransfer(id, 0, false, callback, parameter);
85}
87 void (*callback)(uintptr_t, ssize_t), uintptr_t parameter,
88 bool producerAlreadyStopped) {
90 if (!m_Cancellations.tryAcquire(admission))
91 panic("xHCI: interrupt cancellation raced controller destruction");
92 if (!producerAlreadyStopped)
93 cancelTransfer(token.transaction, token.generation, true, callback, parameter);
94 return m_Deliveries.cancelSubscription(token.transaction, token.generation);
95}
96void Xhci::shutdown() {
97 if (m_Shutdown)
98 return;
99 {
100 LockGuard<Mutex> lock(m_Lock);
101 m_PortsClosing = true;
102 for (size_t i = 0; i < m_PortCount; ++i)
103 m_PortChanges[i].stopAfterQuiesce();
104 }
107 m_Submissions.closeAndWait();
108 {
109 LockGuard<Mutex> lock(m_Lock);
110 if (!halt())
111 panic("xHCI: controller teardown could not halt DMA");
112 m_Online = false;
113 m_Stopping = true;
114 for (auto* transaction : m_Transactions)
115 if (transaction && transaction->submitted)
116 finishLocked(*transaction, -TransactionError, false);
117 }
118 if (m_Irq && !Machine::instance().getIrqManager()->unregisterHandler(m_Irq, this))
119 panic("xHCI: interrupt retirement failed");
120 m_Irq = 0;
121 m_Cancellations.closeAndWait();
122 m_Transfers.closeAndWait();
123 {
124 LockGuard<Mutex> lock(m_Lock);
125 m_DeliveryStopping = true;
126 m_DeliveryReady.release();
127 }
128 if (m_DeliveryThread && !m_DeliveryThread->joinForCompletion())
129 panic("xHCI: callback worker did not drain");
130 m_DeliveryThread = nullptr;
131 for (auto* transaction : m_Transactions)
132 delete transaction;
133 for (auto& slot : m_Slots)
134 for (auto* endpoint : slot.endpoints)
135 delete endpoint;
136 for (auto* scratch : m_Scratch)
137 delete scratch;
138 if (m_HardwareOwned) {
139 write64(m_Op + 0x30, 0);
140 write64(m_Op + 0x18, 0);
141 write(m_Runtime + 0x28, 0);
142 write64(m_Runtime + 0x30, 0);
143 write64(m_Runtime + 0x38, 0);
144 }
145 if (m_HardwareOwned && !PciBus::instance().updateCommand(m_Pci, 4, 0x400))
146 panic("xHCI: teardown could not disable PCI DMA and INTx");
147 m_Shutdown = true;
148}
MUST_USE_RESULT bool tryAcquire(Lease &lease)
virtual void destroy()
void release(size_t n=1)
Definition Semaphore.cc:546
bool joinForCompletion()
Definition Thread.cc:2771
bool cancelSubscription(uintptr_t transaction, size_t subscription)
void disconnectAllDevices()
Definition UsbHub.cc:216
bool cancelInterruptInAndDrain(const UsbInterruptInToken &, void(*)(uintptr_t, ssize_t), uintptr_t, bool producerAlreadyStopped) override
void cancelAsyncAndDrain(uintptr_t transaction, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter) override
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117