8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/compiler.h"
10#include "pedigree/kernel/utilities/utility.h"
12#include "modules/system/usb/UsbConstants.h"
13#include "modules/system/usb/UsbDescriptors.h"
14#include "modules/system/usb/UsbDevice.h"
15#include "modules/system/usb/UsbHub.h"
18constexpr size_t MaxTransactions = 2;
19constexpr size_t MaxTransfers = 2;
20constexpr size_t MaxTraceEvents = 5;
22enum class TraceEvent { ControlSetup, ControlStatus, ControlComplete, BulkData, BulkComplete };
24struct RecordedTransfer {
25 RecordedTransfer() : toggle(false), pid(UsbPidOut), buffer(0), bytes(0) {}
33struct RecordedTransaction {
34 RecordedTransaction() : endpoint(), transfers(), transferCount(0), completed(false) {}
37 RecordedTransfer transfers[MaxTransfers];
42class ScriptedEndpointHaltHub final :
public UsbHub {
44 explicit ScriptedEndpointHaltHub(ssize_t controlResult)
47 m_TransactionCount(0),
50 m_Setup(0, 0, 0, 0, 0),
51 m_ControlResult(controlResult),
57 size_t bytes)
override {
58 RecordedTransaction* recorded = transactionFor(transaction);
59 if (!recorded || recorded->transferCount >= MaxTransfers) {
64 RecordedTransfer& transfer = recorded->transfers[recorded->transferCount++];
65 transfer.toggle = toggle;
67 transfer.buffer = buffer;
68 transfer.bytes = bytes;
70 const size_t transactionIndex = transaction - 1;
71 if (transactionIndex == 0 && recorded->transferCount == 1) {
72 appendTrace(TraceEvent::ControlSetup);
73 }
else if (transactionIndex == 0 && recorded->transferCount == 2) {
74 appendTrace(TraceEvent::ControlStatus);
75 }
else if (transactionIndex == 1 && recorded->transferCount == 1) {
76 appendTrace(TraceEvent::BulkData);
83 if (m_TransactionCount >= MaxTransactions) {
85 return static_cast<uintptr_t
>(-1);
88 RecordedTransaction& transaction = m_Transactions[m_TransactionCount];
89 transaction.endpoint = endpoint;
90 return ++m_TransactionCount;
93 bool doAsync(uintptr_t transaction,
void (*callback)(uintptr_t, ssize_t),
94 uintptr_t parameter)
override {
95 RecordedTransaction* recorded = transactionFor(transaction);
96 if (!recorded || recorded->completed || !callback) {
101 const size_t transactionIndex = transaction - 1;
103 if (transactionIndex == 0) {
104 captureControlTransaction(*recorded);
105 result = m_ControlResult;
106 appendTrace(TraceEvent::ControlComplete);
107 }
else if (transactionIndex == 1) {
108 if (recorded->transferCount != 1) {
111 result =
static_cast<ssize_t
>(recorded->transfers[0].bytes);
113 appendTrace(TraceEvent::BulkComplete);
118 recorded->completed =
true;
119 for (
size_t i = 0; i < recorded->transferCount; ++i) {
120 recorded->transfers[i].buffer = 0;
124 callback(parameter, result);
144 bool matches(uint16_t endpointIndex, uint8_t bulkEndpoint, UsbPid bulkPid,
145 bool bulkToggle)
const {
146 if (!m_Valid || m_TransactionCount != MaxTransactions || m_Callbacks != MaxTransactions ||
147 m_Cancellations || m_TraceCount != MaxTraceEvents) {
151 const TraceEvent expectedTrace[MaxTraceEvents] = {
152 TraceEvent::ControlSetup, TraceEvent::ControlStatus, TraceEvent::ControlComplete,
153 TraceEvent::BulkData, TraceEvent::BulkComplete};
154 for (
size_t i = 0; i < MaxTraceEvents; ++i) {
155 if (m_Trace[i] != expectedTrace[i]) {
160 const RecordedTransaction& control = m_Transactions[0];
161 const RecordedTransaction& bulk = m_Transactions[1];
162 if (!control.completed || control.endpoint.nEndpoint != 0 || control.transferCount != 2 ||
163 control.transfers[0].toggle || control.transfers[0].pid != UsbPidSetup ||
164 control.transfers[0].bytes !=
sizeof(
UsbDevice::Setup) || !control.transfers[1].toggle ||
165 control.transfers[1].pid != UsbPidIn || control.transfers[1].bytes != 0) {
169 if (m_Setup.nRequestType != UsbRequestRecipient::Endpoint ||
170 m_Setup.nRequest != UsbRequest::ClearFeature || m_Setup.nValue != 0 ||
171 m_Setup.nIndex != endpointIndex || m_Setup.nLength != 0) {
175 return bulk.completed && bulk.endpoint.nEndpoint == bulkEndpoint && bulk.transferCount == 1 &&
176 bulk.transfers[0].toggle == bulkToggle && bulk.transfers[0].pid == bulkPid &&
177 bulk.transfers[0].bytes == 1 && buffersRetired();
182 uintptr_t,
bool)
override {
188 RecordedTransaction* transactionFor(uintptr_t transaction) {
189 if (!transaction || transaction > m_TransactionCount) {
192 return &m_Transactions[transaction - 1];
195 void appendTrace(TraceEvent event) {
196 if (m_TraceCount >= MaxTraceEvents) {
200 m_Trace[m_TraceCount++] = event;
203 void captureControlTransaction(RecordedTransaction& transaction) {
204 if (transaction.transferCount != 2 || !transaction.transfers[0].buffer ||
209 MemoryCopy(&m_Setup,
reinterpret_cast<void*
>(transaction.transfers[0].buffer),
sizeof(m_Setup));
212 bool buffersRetired()
const {
213 for (
size_t i = 0; i < m_TransactionCount; ++i) {
214 for (
size_t j = 0; j < m_Transactions[i].transferCount; ++j) {
215 if (m_Transactions[i].transfers[j].buffer) {
223 RecordedTransaction m_Transactions[MaxTransactions];
224 size_t m_TransactionCount;
225 TraceEvent m_Trace[MaxTraceEvents];
228 ssize_t m_ControlResult;
230 size_t m_Cancellations;
234class EndpointHaltTestDevice final :
public UsbDevice {
236 explicit EndpointHaltTestDevice(
UsbHub* hub) :
UsbDevice(hub, 1, HighSpeed) {}
238 bool clearHalt(Endpoint* endpoint) {
242 ssize_t bulkIn(Endpoint* endpoint, uintptr_t buffer,
size_t bytes) {
243 return syncIn(endpoint, buffer, bytes);
246 ssize_t bulkOut(Endpoint* endpoint, uintptr_t buffer,
size_t bytes) {
247 return syncOut(endpoint, buffer, bytes);
253 ByteSet(&descriptor, 0,
sizeof(descriptor));
254 descriptor.nLength =
sizeof(descriptor);
255 descriptor.nType = UsbDescriptor::Endpoint;
256 descriptor.nEndpoint = endpoint;
257 descriptor.bDirection = in;
258 descriptor.nTransferType = UsbDevice::Endpoint::Bulk;
259 descriptor.nMaxPacketSize = 64;
263bool outEndpointSuccess() {
264 ScriptedEndpointHaltHub hub(8);
265 EndpointHaltTestDevice device(&hub);
268 endpoint.bDataToggle =
true;
269 alignas(16) uint8_t buffer[16] = {};
271 const bool cleared = device.clearHalt(&endpoint);
272 const ssize_t transferred = device.bulkOut(&endpoint,
reinterpret_cast<uintptr_t
>(buffer), 1);
273 const bool passed = cleared && transferred == 1 && hub.matches(0x02, 2, UsbPidOut,
false);
275 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-clear-halt-out-endpoint-data0");
278 "HOSTED-WAIT-TEST: FAIL usb-clear-halt-out-endpoint-data0: OUT clear did not use "
279 "endpoint address 0x02 and restart bulk traffic at DATA0");
284bool inEndpointSuccess() {
285 ScriptedEndpointHaltHub hub(8);
286 EndpointHaltTestDevice device(&hub);
289 endpoint.bDataToggle =
true;
290 alignas(16) uint8_t buffer[16] = {};
292 const bool cleared = device.clearHalt(&endpoint);
293 const ssize_t transferred = device.bulkIn(&endpoint,
reinterpret_cast<uintptr_t
>(buffer), 1);
294 const bool passed = cleared && transferred == 1 && hub.matches(0x83, 3, UsbPidIn,
false);
296 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-clear-halt-in-endpoint-data0");
299 "HOSTED-WAIT-TEST: FAIL usb-clear-halt-in-endpoint-data0: IN clear did not use endpoint "
300 "address 0x83 and restart bulk traffic at DATA0");
305bool failedClearPreservesToggle() {
306 ScriptedEndpointHaltHub hub(-TransactionError);
307 EndpointHaltTestDevice device(&hub);
310 endpoint.bDataToggle =
true;
311 alignas(16) uint8_t buffer[16] = {};
313 const bool cleared = device.clearHalt(&endpoint);
314 const bool preserved = endpoint.bDataToggle;
315 const ssize_t transferred = device.bulkOut(&endpoint,
reinterpret_cast<uintptr_t
>(buffer), 1);
317 !cleared && preserved && transferred == 1 && hub.matches(0x02, 2, UsbPidOut,
true);
319 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-clear-halt-failure-preserves-data1");
322 "HOSTED-WAIT-TEST: FAIL usb-clear-halt-failure-preserves-data1: failed ClearFeature "
323 "changed the endpoint toggle or reordered the next transfer");
329EXPORTED_PUBLIC
bool runHostedUsbEndpointHaltRegressions() {
330 const bool outPassed = outEndpointSuccess();
331 const bool inPassed = inEndpointSuccess();
332 const bool failurePassed = failedClearPreservesToggle();
333 return outPassed && inPassed && failurePassed;
bool clearEndpointHalt(Endpoint *pEndpoint)
Clears a halt on the given endpoint.
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)=0
Adds a new transfer to an existent transaction.
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)=0
Creates a new transaction with the given endpoint data.
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)=0
Gets a UsbDevice from a given vendor:product pair.
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)=0
virtual MUST_USE_RESULT bool addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes, void(*pCallback)(uintptr_t, ssize_t), UsbInterruptInHandle &handle, uintptr_t pParam=0)=0
Adds an owned recurring interrupt-IN transaction.
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)=0
virtual MUST_USE_RESULT bool cancelInterruptInAndDrain(const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter, bool producerAlreadyStopped)=0