The Pedigree Project 0.1
PortChangeRequest.h
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#ifndef USB_HCD_PORTCHANGEREQUEST_H
9#define USB_HCD_PORTCHANGEREQUEST_H
10#include "pedigree/kernel/Atomic.h"
11#include "pedigree/kernel/process/Thread.h"
12#include "pedigree/kernel/process/WaitQueue.h"
13#include "pedigree/kernel/utilities/RequestQueue.h"
14
15#include <config.h>
16
17namespace UsbHcd {
18constexpr size_t EhciRootPortCount = 15;
19constexpr size_t OhciRootPortCount = 15;
20constexpr size_t UhciRootPortCount = 8;
21
22inline bool validEhciRootPortCount(size_t count) {
23 return count <= EhciRootPortCount;
24}
25
26inline bool validOhciRootPortCount(size_t count) {
27 return count && count <= OhciRootPortCount;
28}
29
30inline bool validUhciRootPortCount(size_t count) {
31 return count <= UhciRootPortCount;
32}
33
37template <typename T>
38inline T selectiveW1cValue(T status, T changeMask, T acknowledgeMask) {
39 return static_cast<T>((status & ~changeMask) | (acknowledgeMask & changeMask));
40}
41
48 public:
50 for (size_t i = 0; i < EhciRootPortCount; ++i) {
51 m_Generations[i] = 0;
52 }
53 }
54
55 void defer(size_t port, size_t generation) {
56 if (port < EhciRootPortCount && m_Generations[port] < generation) {
57 m_Generations[port] = generation;
58 }
59 }
60
61 size_t release(size_t port) {
62 if (port >= EhciRootPortCount) {
63 return 0;
64 }
65
66 const size_t generation = m_Generations[port];
67 m_Generations[port] = 0;
68 return generation;
69 }
70
71 bool empty() const {
72 for (size_t i = 0; i < EhciRootPortCount; ++i) {
73 if (m_Generations[i]) {
74 return false;
75 }
76 }
77 return true;
78 }
79
80 private:
81 size_t m_Generations[EhciRootPortCount];
82};
83
95 public:
96 enum class Result {
97 Accepted,
98 Coalesced,
99 TokenBusy,
100 QueueStopped,
101 QueueFull,
102 InvalidPriority,
103 };
104
105 struct Observation {
106 Observation(Result result, size_t generation) : result(result), generation(generation) {}
107
108 Result result;
109 size_t generation;
110 };
111
113 public:
114 Completion(PortChangeRequest& request, size_t generation)
115 : m_Request(request),
116 m_Generation(generation),
117 m_Ready(request.waitUntilAcknowledged(generation)) {}
118
119 ~Completion() {
120 m_Request.consume(m_Generation);
121 }
122
123 explicit operator bool() const {
124 return m_Ready;
125 }
126
127 private:
128 NOT_COPYABLE_OR_ASSIGNABLE(Completion);
129 PortChangeRequest& m_Request;
130 size_t m_Generation;
131 bool m_Ready;
132 };
133
135 : m_Request(requestReleased, this),
136 m_Queue(nullptr),
137 m_Priority(0),
138 m_Configured(0),
139 m_Observed(0),
140 m_Acknowledged(0),
141 m_Consumed(0),
142 m_Stopping(0),
143 m_ReleaseFailures(0),
144 m_AcknowledgementWaiters() {
145 for (size_t i = 0; i < 7; ++i) {
146 m_Parameters[i] = 0;
147 }
148 }
149
150 bool configure(RequestQueue& queue, size_t priority, uint64_t p1 = 0, uint64_t p2 = 0,
151 uint64_t p3 = 0, uint64_t p4 = 0, uint64_t p5 = 0, uint64_t p6 = 0,
152 uint64_t p7 = 0) {
153 if (m_Configured || priority >= REQUEST_QUEUE_NUM_PRIORITIES || !m_Request.isAvailable()) {
154 return false;
155 }
156
157 m_Queue = &queue;
158 m_Priority = priority;
159 m_Parameters[0] = p1;
160 m_Parameters[1] = p2;
161 m_Parameters[2] = p3;
162 m_Parameters[3] = p4;
163 m_Parameters[4] = p5;
164 m_Parameters[5] = p6;
165 m_Parameters[6] = p7;
166 m_Configured = 1;
167 return true;
168 }
169
170 Observation observe() {
171#if !THREADS
172 return Observation(Result::QueueStopped, 0);
173#else
174 if (!m_Configured || m_Stopping) {
175 return Observation(Result::QueueStopped, 0);
176 }
177
178 const size_t generation = (m_Observed += 1);
179 auto result = publishGeneration(generation, true);
180 return Observation(result, generation);
181#endif
182 }
183
184 void acknowledge(size_t generation) {
185#if THREADS
186 auto guard = m_AcknowledgementWaiters.acquire();
187#endif
188 advance(m_Acknowledged, generation);
189#if THREADS
190 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(this));
191#endif
192 }
193
202#if THREADS
203 auto guard = m_AcknowledgementWaiters.acquire();
204#endif
205 m_Stopping = 1;
206#if THREADS
207 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(this));
208#endif
209 }
210
211 void cancel(size_t generation) {
212 consume(generation);
213 }
214
215 bool isIdle() const {
216 return m_Request.isAvailable() && (m_Stopping || m_Consumed >= m_Observed);
217 }
218
219 size_t observedGeneration() const {
220 return m_Observed;
221 }
222
223 static bool canAcknowledge(Result result) {
224 return result == Result::Accepted || result == Result::Coalesced;
225 }
226
227 bool hasPublicationFailure() const {
228 return static_cast<size_t>(m_ReleaseFailures) != 0;
229 }
230
231 private:
232 static void advance(Atomic<size_t>& value, size_t target) {
233 while (value < target) {
234 const size_t current = value;
235 if (current >= target || value.compareAndSwap(current, target)) {
236 return;
237 }
238 }
239 }
240
241 static Result translate(RequestQueue::PreallocatedPublishResult result) {
242 using QueueResult = RequestQueue::PreallocatedPublishResult;
243 switch (result) {
244 case QueueResult::Accepted:
245 return Result::Accepted;
246 case QueueResult::QueueStopped:
247 return Result::QueueStopped;
248 case QueueResult::QueueFull:
249 return Result::QueueFull;
250 case QueueResult::InvalidPriority:
251 return Result::InvalidPriority;
252 case QueueResult::TokenBusy:
253 return Result::TokenBusy;
254 }
255 return Result::TokenBusy;
256 }
257
258 static void requestReleased(void* context) {
259 reinterpret_cast<PortChangeRequest*>(context)->released();
260 }
261
262 void released() {
263#if THREADS
264 // stopAfterQuiesce must either precede this publication or wait until
265 // it is fully visible to RequestQueue teardown.
266 auto guard = m_AcknowledgementWaiters.acquire();
267#endif
268 if (m_Stopping) {
269 return;
270 }
271
272 const size_t observed = m_Observed;
273 if (observed <= m_Consumed) {
274 return;
275 }
276
277 const Result result = publishGeneration(observed, false);
278 if (result != Result::Accepted && result != Result::Coalesced) {
279 m_ReleaseFailures += 1;
280 }
281 }
282
283 Result publishGeneration(size_t generation, bool tryIdle) {
284 auto result = m_Queue->republishPreallocatedWhileReleasing(
285 m_Request, m_Priority, m_Parameters[0], m_Parameters[1], m_Parameters[2], m_Parameters[3],
286 m_Parameters[4], m_Parameters[5], m_Parameters[6], generation);
287 if (tryIdle && result == RequestQueue::PreallocatedPublishResult::TokenBusy) {
288 result = m_Queue->publishPreallocated(m_Request, m_Priority, m_Parameters[0], m_Parameters[1],
289 m_Parameters[2], m_Parameters[3], m_Parameters[4],
290 m_Parameters[5], m_Parameters[6], generation);
291 }
292
293 if (result == RequestQueue::PreallocatedPublishResult::TokenBusy) {
294 return Result::Coalesced;
295 }
296 return translate(result);
297 }
298
299 bool waitUntilAcknowledged(size_t generation) {
300#if THREADS
301 while (true) {
302 auto guard = m_AcknowledgementWaiters.acquire();
303 if (m_Stopping) {
304 return false;
305 }
306 if (m_Acknowledged >= generation) {
307 return true;
308 }
309
310 const WaitQueue::WakeReason reason = guard.waitForCompletion(
311 WaitQueue::Channel(this), Thread::CallbackDrain, reinterpret_cast<uintptr_t>(this));
312 (void)reason;
313 }
314#else
315 return !m_Stopping && m_Acknowledged >= generation;
316#endif
317 }
318
319 void consume(size_t generation) {
320 advance(m_Consumed, generation);
321 }
322
324 RequestQueue* m_Queue;
325 size_t m_Priority;
326 uint64_t m_Parameters[7];
327 Atomic<size_t> m_Configured;
328 Atomic<size_t> m_Observed;
329 Atomic<size_t> m_Acknowledged;
330 Atomic<size_t> m_Consumed;
331 Atomic<size_t> m_Stopping;
332 Atomic<size_t> m_ReleaseFailures;
333 WaitQueue m_AcknowledgementWaiters;
334};
335} // namespace UsbHcd
336
337#endif
MUST_USE_RESULT PreallocatedPublishResult republishPreallocatedWhileReleasing(PreallocatedRequest &request, size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
MUST_USE_RESULT PreallocatedPublishResult publishPreallocated(PreallocatedRequest &request, size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
MUST_USE_RESULT WakeReason waitForCompletion(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0)
Definition WaitQueue.cc:116