The Pedigree Project 0.1
XhciDevice.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/panic.h"
5#include "pedigree/kernel/time/Time.h"
6#include "pedigree/kernel/utilities/utility.h"
7
8#include "Xhci.h"
9using namespace XhciHw;
10uint32_t* Xhci::inputContext(size_t index) {
11 return reinterpret_cast<uint32_t*>(static_cast<uint8_t*>(m_Input.virtualAddress()) +
12 index * m_ContextSize);
13}
14uint32_t* Xhci::outputContext(uint8_t slot, size_t index) {
15 return reinterpret_cast<uint32_t*>(static_cast<uint8_t*>(m_Slots[slot].output.virtualAddress()) +
16 index * m_ContextSize);
17}
18uint8_t Xhci::findSlot(const UsbEndpoint& endpoint) const {
19 if (endpoint.nAddress >= 128 || endpoint.nRootPort >= m_PortCount || endpoint.nHubAddress)
20 return 0;
21 const uint8_t slot =
22 endpoint.nAddress ? m_Addresses[endpoint.nAddress] : m_PortSlots[endpoint.nRootPort];
23 const uint32_t status = read(m_Op + 0x400 + endpoint.nRootPort * 16);
24 if (!slot || slot > m_SlotCount || m_Slots[slot].port != endpoint.nRootPort ||
25 m_Slots[slot].generation != endpoint.nRootPortGeneration ||
26 currentRootPortGeneration(endpoint.nRootPort) != endpoint.nRootPortGeneration ||
27 m_Ports[endpoint.nRootPort].changePending || !(status & 1U) || (status & (1U << 17)))
28 return 0;
29 return slot;
30}
31bool Xhci::prepareDevice(uint8_t address, const UsbEndpoint& endpoint) {
33 if (!m_Submissions.tryAcquire(admission) || !address || address >= 128 ||
34 endpoint.nRootPort >= m_PortCount || endpoint.nHubAddress)
35 return false;
36 LockGuard<Mutex> configuration(m_ConfigurationLock);
37 const uint8_t port = endpoint.nRootPort;
38 if (m_Addresses[address] || m_PortSlots[port])
39 return false;
40 uint8_t slotId = 0;
41 if (!command({0, 0, (9U << 10) | (uint32_t{m_Ports[port].slotType} << 16)}, &slotId))
42 return false;
43 if (!slotId || slotId > m_SlotCount || m_Slots[slotId].logical)
44 panic("xHCI: controller returned invalid slot ownership");
45 auto& slot = m_Slots[slotId];
46 slot.logical = address;
47 slot.port = port;
48 slot.generation = endpoint.nRootPortGeneration;
49 slot.speedId = (read(m_Op + 0x400 + port * 16) >> 10) & 15U;
50 m_Addresses[address] = slotId;
51 m_PortSlots[port] = slotId;
52 if (!m_Ports[port].validSpeed[slot.speedId] || !allocate(slot.output, 1))
53 return false;
54 auto* ep = new Endpoint;
55 slot.endpoints[1] = ep;
56 ep->description = endpoint;
57 if (!ep->ring.initialise())
58 return false;
59 static_cast<uint64_t*>(m_Dcbaa.virtualAddress())[slotId] = slot.output.physicalAddress();
60 ByteSet(m_Input.virtualAddress(), 0, PageBytes);
61 inputContext(0)[1] = 3;
62 uint32_t* device = inputContext(1);
63 device[0] = (uint32_t{slot.speedId} << 20) | (1U << 27);
64 device[1] = (uint32_t{port + 1U} << 16);
65 uint32_t* control = inputContext(2);
66 control[1] = (3U << 1) | (4U << 3) | (endpoint.nMaxPacketSize << 16);
67 const uint64_t dequeue = ep->ring.enqueuePointer();
68 control[2] = dequeue;
69 control[3] = dequeue >> 32;
70 control[4] = 8;
71 FENCE();
72 if (!command({m_Input.physicalAddress(), 0, (11U << 10) | (1U << 9) | (uint32_t{slotId} << 24)}))
73 return false;
74 NOTICE("xHCI: logical address " << Dec << address << " slot " << slotId << " root port "
75 << port + 1U << " USB" << m_Ports[port].major << " speed ID "
76 << slot.speedId << Hex);
77 return true;
78}
79bool Xhci::addressDevice(uint8_t address, const UsbEndpoint& endpoint) {
81 if (!m_Submissions.tryAcquire(admission))
82 return false;
83 LockGuard<Mutex> configuration(m_ConfigurationLock);
84 const uint8_t slotId = findSlot(endpoint);
85 if (!slotId || m_Slots[slotId].logical != address)
86 return false;
87 auto& slot = m_Slots[slotId];
88 auto* ep = slot.endpoints[1];
89 ByteSet(m_Input.virtualAddress(), 0, PageBytes);
90 inputContext(0)[1] = 2;
91 MemoryCopy(inputContext(2), outputContext(slotId, 1), m_ContextSize);
92 inputContext(2)[0] &= ~7U;
93 inputContext(2)[1] = (inputContext(2)[1] & 0xffffU) | (endpoint.nMaxPacketSize << 16);
94 if (ep->description.nMaxPacketSize != endpoint.nMaxPacketSize &&
95 !command({m_Input.physicalAddress(), 0, (13U << 10) | (uint32_t{slotId} << 24)}))
96 return false;
97 ep->description.nMaxPacketSize = endpoint.nMaxPacketSize;
98 inputContext(0)[1] = 3;
99 MemoryCopy(inputContext(1), outputContext(slotId, 0), m_ContextSize);
100 inputContext(1)[3] = 0;
101 const uint64_t dequeue = ep->ring.enqueuePointer();
102 inputContext(2)[2] = dequeue;
103 inputContext(2)[3] = dequeue >> 32;
104 FENCE();
105 return command({m_Input.physicalAddress(), 0, (11U << 10) | (uint32_t{slotId} << 24)});
106}
107Xhci::Endpoint* Xhci::ensureEndpoint(uint8_t slotId, const UsbEndpoint& description) {
108 auto& slot = m_Slots[slotId];
109 const uint8_t dci = description.nEndpoint ? description.nEndpoint * 2 + description.nIn : 1;
110 if (dci >= 32 || description.nStreams || description.nMaxBurst > 15 ||
111 description.nTransferType == 1 || description.nMaxPacketSize > 1024)
112 return nullptr;
113 if (slot.endpoints[dci]) {
114 auto* endpoint = slot.endpoints[dci];
115 if (endpoint->needsReset && !resetEndpointHardware(slotId, dci, false))
116 return nullptr;
117 return endpoint;
118 }
119 if (!description.nEndpoint || (description.nTransferType != 2 && description.nTransferType != 3))
120 return nullptr;
121 auto* endpoint = new Endpoint;
122 endpoint->description = description;
123 if (!endpoint->ring.initialise()) {
124 delete endpoint;
125 return nullptr;
126 }
127 uint32_t interval = 0;
128 if (description.nTransferType == 3) {
129 if (!description.nInterval) {
130 delete endpoint;
131 return nullptr;
132 }
133 if (description.speed == HighSpeed || description.speed == SuperSpeed) {
134 if (description.nInterval > 16) {
135 delete endpoint;
136 return nullptr;
137 }
138 interval = description.nInterval - 1;
139 } else {
140 interval = 3;
141 for (size_t frames = description.nInterval; frames >= 2; frames /= 2)
142 ++interval;
143 }
144 }
145 ByteSet(m_Input.virtualAddress(), 0, PageBytes);
146 inputContext(0)[1] = 1U | (1U << dci);
147 MemoryCopy(inputContext(1), outputContext(slotId, 0), m_ContextSize);
148 const size_t entries = dci > slot.contextEntries ? dci : slot.contextEntries;
149 inputContext(1)[0] = (inputContext(1)[0] & ~(31U << 27)) | (entries << 27);
150 inputContext(1)[3] = 0;
151 uint32_t* context = inputContext(dci + 1);
152 size_t esit = description.nTransferType == 3
153 ? (description.nBytesPerInterval ? description.nBytesPerInterval
154 : description.nMaxPacketSize)
155 : 0;
156 context[0] = (interval << 16) | ((esit >> 16) << 24);
157 const uint32_t type = (description.nTransferType == 2 ? 2U : 3U) + (description.nIn ? 4U : 0U);
158 context[1] = (3U << 1) | (type << 3) | (uint32_t{description.nMaxBurst} << 8) |
159 (description.nMaxPacketSize << 16);
160 const uint64_t dequeue = endpoint->ring.enqueuePointer();
161 context[2] = dequeue;
162 context[3] = dequeue >> 32;
163 context[4] = description.nMaxPacketSize | ((esit & 0xffffU) << 16);
164 FENCE();
165 if (!command({m_Input.physicalAddress(), 0, (12U << 10) | (uint32_t{slotId} << 24)})) {
166 delete endpoint;
167 return nullptr;
168 }
169 slot.endpoints[dci] = endpoint;
170 slot.contextEntries = entries;
171 return endpoint;
172}
173bool Xhci::resetEndpointHardware(uint8_t slotId, uint8_t dci, bool stop) {
174 auto* endpoint = m_Slots[slotId].endpoints[dci];
175 if (!endpoint)
176 return false;
177 const uint32_t state = outputContext(slotId, dci)[0] & 7U;
178 const uint32_t target = (uint32_t{slotId} << 24) | (uint32_t{dci} << 16);
179 if (state == 2) {
180 if (!command({0, 0, (14U << 10) | target}))
181 return false;
182 } else if (state == 1 && stop) {
183 if (!command({0, 0, (15U << 10) | target}))
184 return false;
185 } else if (state != 3)
186 return false;
187 if (!command({endpoint->ring.enqueuePointer(), 0, (16U << 10) | target}))
188 return false;
189 endpoint->needsReset = false;
190 return true;
191}
192bool Xhci::resetEndpoint(const UsbEndpoint& endpoint) {
193 OperationBarrier::Lease admission;
194 if (!m_Submissions.tryAcquire(admission))
195 return false;
196 LockGuard<Mutex> configuration(m_ConfigurationLock);
197 const uint8_t slot = findSlot(endpoint);
198 const uint8_t dci = endpoint.nEndpoint ? endpoint.nEndpoint * 2 + endpoint.nIn : 1;
199 if (!slot || dci >= 32 || !m_Slots[slot].endpoints[dci])
200 return false;
201 return resetEndpointHardware(slot, dci, true);
202}
203void Xhci::releaseDeviceAddress(uint8_t address) {
204 LockGuard<Mutex> configuration(m_ConfigurationLock);
205 const uint8_t slotId = m_Addresses[address];
206 if (!slotId)
207 return;
208 const bool retired = m_Online && command({0, 0, (10U << 10) | (uint32_t{slotId} << 24)});
209 if (m_Online && !retired) {
210 LockGuard<Mutex> lock(m_Lock);
211 failLocked();
212 }
213 LockGuard<Mutex> lock(m_Lock);
214 auto& slot = m_Slots[slotId];
215 for (auto*& endpoint : slot.endpoints) {
216 if (endpoint && endpoint->active)
217 panic("xHCI: logical address retired with an owned transfer");
218 delete endpoint;
219 endpoint = nullptr;
220 }
221 if (m_Dcbaa)
222 static_cast<uint64_t*>(m_Dcbaa.virtualAddress())[slotId] = 0;
223 m_PortSlots[slot.port] = 0;
224 m_Addresses[address] = 0;
225 slot.output.free();
226 slot.logical = 0;
227 slot.contextEntries = 1;
228#if PEDIGREE_USB_SMOKE_TESTS
229 if (retired && !m_Failed)
230 NOTICE("XHCI-SMOKE: slot-retired logical=" << Hex << address);
231#endif
232}
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
MUST_USE_RESULT bool tryAcquire(Lease &lease)
void releaseDeviceAddress(uint8_t address) override
size_t currentRootPortGeneration(size_t port) const override
Definition XhciEvent.cc:119
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117
@ Dec
Definition Log.h:144
@ Hex
Definition Log.h:142