8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/compiler.h"
10#include "pedigree/kernel/utilities/PointerGuard.h"
11#include "pedigree/kernel/utilities/utility.h"
13#include "modules/drivers/common/usb-mass-storage/UsbMassStorageDevice.h"
14#include "modules/system/usb/UsbConstants.h"
15#include "modules/system/usb/UsbDescriptors.h"
16#include "modules/system/usb/UsbHub.h"
23 device.m_pInEndpoint = in;
24 device.m_pOutEndpoint = out;
28 device.m_nUnits = units;
33constexpr size_t DataBytes = 512;
34constexpr size_t MaxTransactions = 64;
35constexpr size_t MaxTransfers = 8;
36constexpr size_t MaxCbws = 16;
37constexpr int AnyToggle = -1;
38constexpr uint32_t CbwSignature = HOST_TO_LITTLE32(0x43425355);
39constexpr uint32_t CswSignature = HOST_TO_LITTLE32(0x53425355);
41constexpr uint8_t WriteCdb[10] = {0x2a, 0, 0, 0, 0, 8, 0, 0, 1, 0};
60static_assert(
sizeof(WireCbw) == 31,
"BOT CBW wire size changed");
61static_assert(
sizeof(WireCsw) == 13,
"BOT CSW wire size changed");
63enum class StepKind { Cbw, DataOut, DataIn, Csw, Reset, ClearIn, ClearOut };
64enum class TagReply { Match, Wrong };
68 : kind(StepKind::Cbw),
70 firstToggle(AnyToggle),
71 expectedData(nullptr),
76 signature(CswSignature),
77 tagReply(TagReply::Match),
85 const uint8_t* expectedData;
96struct RecordedTransfer {
97 RecordedTransfer() : toggle(false), pid(UsbPidOut), buffer(0), bytes(0) {}
105struct RecordedTransaction {
106 RecordedTransaction() : endpoint(), transfers(), transferCount(0), completed(false) {}
109 RecordedTransfer transfers[MaxTransfers];
110 size_t transferCount;
114class ScriptedBotHub final :
public UsbHub {
121 m_TransactionCount(0),
129 void expectCbw(uint8_t lun,
const uint8_t* command, uint8_t commandSize,
size_t dataBytes,
130 int firstToggle, ssize_t result = 31,
bool writing =
true) {
131 Step* step = append(StepKind::Cbw, result, firstToggle);
134 step->writing = writing;
136 step->commandSize = commandSize;
137 step->expectedBytes = dataBytes;
138 MemoryCopy(step->command, command, commandSize);
141 void expectDataOut(
const uint8_t* data,
size_t bytes,
int firstToggle, ssize_t result) {
142 Step* step = append(StepKind::DataOut, result, firstToggle);
145 step->expectedData = data;
146 step->expectedBytes = bytes;
149 void expectDataIn(
const uint8_t* data,
size_t bytes, ssize_t result) {
150 Step* step = append(StepKind::DataIn, result, AnyToggle);
151 step->expectedData = data;
152 step->expectedBytes = bytes;
155 void expectCsw(ssize_t result, uint8_t status = 0, uint32_t residue = 0,
156 TagReply tagReply = TagReply::Match, uint32_t signature = CswSignature,
157 int firstToggle = AnyToggle) {
158 Step* step = append(StepKind::Csw, result, firstToggle);
161 step->signature = signature;
162 step->tagReply = tagReply;
163 step->residue = residue;
164 step->status = status;
167 void expectControl(StepKind kind, ssize_t result =
sizeof(
UsbDevice::Setup)) {
168 append(kind, result, AnyToggle);
172 size_t bytes)
override {
173 RecordedTransaction* recorded = transactionFor(transaction);
174 if (!recorded || recorded->transferCount >= MaxTransfers) {
178 RecordedTransfer& transfer = recorded->transfers[recorded->transferCount++];
179 transfer.toggle = toggle;
181 transfer.buffer = buffer;
182 transfer.bytes = bytes;
186 if (m_TransactionCount >= MaxTransactions) {
188 return static_cast<uintptr_t
>(-1);
190 RecordedTransaction& transaction = m_Transactions[m_TransactionCount];
191 transaction.endpoint = endpoint;
192 return ++m_TransactionCount;
195 bool doAsync(uintptr_t transaction,
void (*callback)(uintptr_t, ssize_t),
196 uintptr_t parameter)
override {
197 RecordedTransaction* recorded = transactionFor(transaction);
198 if (!recorded || recorded->completed || !callback) {
203 const size_t index = transaction - 1;
204 Step* step = index < m_StepCount ? &m_Steps[index] :
nullptr;
211 seedCsw(*recorded, step && step->kind == StepKind::Csw ? step : nullptr);
212 if (step && !validate(*recorded, *step))
215 recorded->completed =
true;
216 for (
size_t i = 0; i < recorded->transferCount; ++i)
217 recorded->transfers[i].buffer = 0;
220 callback(parameter, step ? step->result : -TransactionError);
240 bool complete()
const {
241 if (!m_Valid || m_TransactionCount != m_StepCount || m_Callbacks != m_TransactionCount ||
244 for (
size_t i = 0; i < m_TransactionCount; ++i) {
245 if (!m_Transactions[i].completed)
247 for (
size_t j = 0; j < m_Transactions[i].transferCount; ++j) {
248 if (m_Transactions[i].transfers[j].buffer)
255 bool tagsMonotonic()
const {
258 uint32_t previous = LITTLE_TO_HOST32(m_Cbws[0].tag);
261 for (
size_t i = 1; i < m_CbwCount; ++i) {
262 const uint32_t tag = LITTLE_TO_HOST32(m_Cbws[i].tag);
263 if (tag != previous + 1)
270 bool firstTagIs(uint32_t tag)
const {
271 return m_CbwCount && LITTLE_TO_HOST32(m_Cbws[0].tag) == tag;
274 size_t transactionCount()
const {
275 return m_TransactionCount;
280 uintptr_t,
bool)
override {
286 Step* append(StepKind kind, ssize_t result,
int firstToggle) {
287 if (m_StepCount >= MaxTransactions) {
291 Step& step = m_Steps[m_StepCount++];
293 step.result = result;
294 step.firstToggle = firstToggle;
298 RecordedTransaction* transactionFor(uintptr_t transaction) {
299 if (!transaction || transaction > m_TransactionCount)
301 return &m_Transactions[transaction - 1];
304 bool bulkShape(
const RecordedTransaction& transaction, uint8_t endpoint, UsbPid pid,
size_t bytes,
305 int firstToggle)
const {
306 if (transaction.endpoint.nEndpoint != endpoint || !transaction.transferCount)
309 bool toggle = firstToggle > 0;
310 for (
size_t i = 0; i < transaction.transferCount; ++i) {
311 const RecordedTransfer& transfer = transaction.transfers[i];
312 if (transfer.pid != pid || (firstToggle != AnyToggle && transfer.toggle != toggle))
314 total += transfer.bytes;
317 return total == bytes;
320 bool validateCbw(
const RecordedTransaction& transaction,
const Step& step) {
321 if (!bulkShape(transaction, 2, UsbPidOut,
sizeof(WireCbw), step.firstToggle) ||
322 transaction.transferCount != 1 || !transaction.transfers[0].buffer || m_CbwCount >= MaxCbws)
325 WireCbw& cbw = m_Cbws[m_CbwCount++];
326 MemoryCopy(&cbw,
reinterpret_cast<void*
>(transaction.transfers[0].buffer),
sizeof(cbw));
328 if (cbw.signature != CbwSignature || cbw.dataBytes != HOST_TO_LITTLE32(step.expectedBytes) ||
329 cbw.flags != (!step.writing && step.expectedBytes ? 0x80 : 0) || cbw.lun != step.lun ||
330 cbw.commandSize != step.commandSize ||
331 MemoryCompare(cbw.command, step.command, step.commandSize))
333 for (
size_t i = step.commandSize; i <
sizeof(cbw.command); ++i) {
340 bool validateData(
const RecordedTransaction& transaction,
const Step& step)
const {
341 const bool input = step.kind == StepKind::DataIn;
342 if (!bulkShape(transaction, input ? 3 : 2, input ? UsbPidIn : UsbPidOut, step.expectedBytes,
346 for (
size_t i = 0; i < transaction.transferCount; ++i) {
347 const RecordedTransfer& transfer = transaction.transfers[i];
348 if (!transfer.buffer)
351 MemoryCopy(
reinterpret_cast<void*
>(transfer.buffer), step.expectedData + offset,
353 else if (MemoryCompare(
reinterpret_cast<void*
>(transfer.buffer), step.expectedData + offset,
356 offset += transfer.bytes;
361 bool validateCsw(
const RecordedTransaction& transaction,
const Step& step)
const {
362 return bulkShape(transaction, 3, UsbPidIn,
sizeof(WireCsw), step.firstToggle) &&
363 transaction.transferCount == 1 && transaction.transfers[0].buffer;
366 bool validateControl(
const RecordedTransaction& transaction,
const Step& step)
const {
367 if (transaction.endpoint.nEndpoint != 0 || transaction.transferCount != 2 ||
368 transaction.transfers[0].toggle || transaction.transfers[0].pid != UsbPidSetup ||
370 !transaction.transfers[0].buffer || !transaction.transfers[1].toggle ||
371 transaction.transfers[1].pid != UsbPidIn || transaction.transfers[1].bytes)
375 MemoryCopy(&setup,
reinterpret_cast<void*
>(transaction.transfers[0].buffer),
sizeof(setup));
376 if (step.kind == StepKind::Reset) {
377 return setup.nRequestType ==
378 (uint8_t(UsbRequestType::Class) | uint8_t(UsbRequestRecipient::Interface)) &&
379 setup.nRequest == 0xff && setup.nValue == 0 && setup.nIndex == 4 && setup.nLength == 0;
382 const uint16_t endpoint = step.kind == StepKind::ClearIn ? 0x83 : 0x02;
383 return setup.nRequestType == UsbRequestRecipient::Endpoint &&
384 setup.nRequest == UsbRequest::ClearFeature && setup.nValue == 0 &&
385 setup.nIndex == endpoint && setup.nLength == 0;
388 bool validate(
const RecordedTransaction& transaction,
const Step& step) {
391 return validateCbw(transaction, step);
392 case StepKind::DataOut:
393 case StepKind::DataIn:
394 return validateData(transaction, step);
396 return validateCsw(transaction, step);
397 case StepKind::Reset:
398 case StepKind::ClearIn:
399 case StepKind::ClearOut:
400 return validateControl(transaction, step);
405 void seedCsw(RecordedTransaction& transaction,
const Step* step) {
406 if (transaction.endpoint.nEndpoint != 3 || transaction.transferCount != 1 ||
407 transaction.transfers[0].pid != UsbPidIn ||
408 transaction.transfers[0].bytes !=
sizeof(WireCsw) || !transaction.transfers[0].buffer)
412 csw.signature = step ? step->signature : CswSignature;
414 if (step && step->tagReply == TagReply::Wrong)
415 csw.tag = HOST_TO_LITTLE32(LITTLE_TO_HOST32(m_LastTag) + 1);
416 csw.residue = HOST_TO_LITTLE32(step ? step->residue : 0);
417 csw.status = step ? step->status : 0;
418 MemoryCopy(
reinterpret_cast<void*
>(transaction.transfers[0].buffer), &csw,
sizeof(csw));
421 Step m_Steps[MaxTransactions];
423 RecordedTransaction m_Transactions[MaxTransactions];
424 size_t m_TransactionCount;
425 WireCbw m_Cbws[MaxCbws];
429 size_t m_Cancellations;
435 ByteSet(&descriptor, 0,
sizeof(descriptor));
436 descriptor.nLength =
sizeof(descriptor);
437 descriptor.nType = UsbDescriptor::Endpoint;
438 descriptor.nEndpoint = endpoint;
439 descriptor.bDirection = in;
440 descriptor.nTransferType = UsbDevice::Endpoint::Bulk;
441 descriptor.nMaxPacketSize = 64;
447 ByteSet(&descriptor, 0,
sizeof(descriptor));
448 descriptor.nLength =
sizeof(descriptor);
449 descriptor.nType = UsbDescriptor::Interface;
450 descriptor.nInterface = 4;
458 void bindInterface(
Interface* interface) {
459 m_pInterface = interface;
466 base(&hub, 1, HighSpeed),
467 interfaceDesc(interfaceDescriptor()),
468 interface(&interfaceDesc),
469 outDesc(endpointDescriptor(2, false)),
470 out(&outDesc, HighSpeed),
471 inDesc(endpointDescriptor(3, true)),
472 in(&inDesc, HighSpeed),
475 device.bindInterface(&interface);
476 UsbMassStorageBotTestAccess::bind(device, &in, &out);
477 for (
size_t i = 0; i <
sizeof(payload); ++i)
478 payload[i] =
static_cast<uint8_t
>((i * 17) ^ 0x5a);
489 BotTestDevice device;
490 alignas(16) uint8_t payload[DataBytes];
493void expectWrite(BotFixture& fixture,
int cbwToggle,
int dataToggle,
494 ssize_t dataResult = DataBytes) {
495 fixture.hub.expectCbw(3, WriteCdb,
sizeof(WriteCdb), DataBytes, cbwToggle);
496 fixture.hub.expectDataOut(fixture.payload, DataBytes, dataToggle, dataResult);
499bool sendWrite(BotFixture& fixture) {
500 return fixture.device.sendCommand(3,
reinterpret_cast<uintptr_t
>(WriteCdb),
sizeof(WriteCdb),
501 reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
true);
504bool completeDataOut() {
505 auto* fixtureStorage =
new BotFixture;
507 BotFixture& fixture = *fixtureStorage;
508 expectWrite(fixture, 0, 1);
509 fixture.hub.expectCsw(13, 0, 0, TagReply::Match, CswSignature, 0);
510 expectWrite(fixture, 1, 0);
511 fixture.hub.expectCsw(13, 0, 0, TagReply::Match, CswSignature, 1);
513 const bool first = sendWrite(fixture);
514 const bool second = sendWrite(fixture);
515 const bool passed = first && second && fixture.hub.complete() && fixture.hub.tagsMonotonic();
517 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-data-out-complete");
519 ERROR(
"HOSTED-WAIT-TEST: FAIL usb-bot-data-out-complete: exact CBW/data/CSW or tags failed");
524bool shortDataOutResets() {
525 auto* fixtureStorage =
new BotFixture;
527 BotFixture& fixture = *fixtureStorage;
528 expectWrite(fixture, 0, 1, DataBytes / 2);
529 fixture.hub.expectControl(StepKind::Reset);
530 fixture.hub.expectControl(StepKind::ClearIn);
531 fixture.hub.expectControl(StepKind::ClearOut);
533 const bool result = sendWrite(fixture);
535 !result && fixture.hub.complete() && !fixture.in.bDataToggle && !fixture.out.bDataToggle;
537 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-data-out-short-reset");
540 "HOSTED-WAIT-TEST: FAIL usb-bot-data-out-short-reset: short OUT was accepted or recovery "
546bool invalidCbwCase(ssize_t result,
bool followWithValidCommand) {
547 auto* fixtureStorage =
new BotFixture;
549 BotFixture& fixture = *fixtureStorage;
550 fixture.hub.expectCbw(3, WriteCdb,
sizeof(WriteCdb), DataBytes, 0, result);
551 fixture.hub.expectControl(StepKind::Reset);
552 fixture.hub.expectControl(StepKind::ClearIn);
553 fixture.hub.expectControl(StepKind::ClearOut);
554 if (followWithValidCommand) {
555 expectWrite(fixture, 0, 1);
556 fixture.hub.expectCsw(13, 0, 0, TagReply::Match, CswSignature, 0);
559 const bool failed = !sendWrite(fixture);
560 const bool followUp = !followWithValidCommand || sendWrite(fixture);
561 return failed && followUp && fixture.hub.complete() &&
562 (!followWithValidCommand || (fixture.hub.firstTagIs(1) && fixture.hub.tagsMonotonic()));
565bool invalidCbwResets() {
566 const bool shortResult = invalidCbwCase(
sizeof(WireCbw) - 1,
true);
567 const bool stalledResult = invalidCbwCase(-Stall,
false);
568 const bool passed = shortResult && stalledResult;
570 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-cbw-exact-reset");
573 "HOSTED-WAIT-TEST: FAIL usb-bot-cbw-exact-reset: short or stalled CBW did not perform "
574 "full reset recovery");
579bool stalledDataOutUsesCsw() {
580 auto* fixtureStorage =
new BotFixture;
582 BotFixture& fixture = *fixtureStorage;
583 expectWrite(fixture, 0, 1, -Stall);
584 fixture.hub.expectControl(StepKind::ClearOut);
585 fixture.hub.expectCsw(13, 0, 0, TagReply::Match, CswSignature, 0);
587 const bool result = sendWrite(fixture);
588 const bool passed = result && fixture.hub.complete() && !fixture.out.bDataToggle;
590 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-data-out-stall-csw");
593 "HOSTED-WAIT-TEST: FAIL usb-bot-data-out-stall-csw: recovered OUT STALL did not use the "
594 "authoritative CSW");
599bool stalledDataOutCswCase(uint8_t status, uint32_t residue) {
600 auto* fixtureStorage =
new BotFixture;
602 BotFixture& fixture = *fixtureStorage;
603 expectWrite(fixture, 0, 1, -Stall);
604 fixture.hub.expectControl(StepKind::ClearOut);
605 fixture.hub.expectCsw(13, status, residue, TagReply::Match, CswSignature, 0);
606 return !sendWrite(fixture) && fixture.hub.complete();
609bool stalledDataOutHonoursCswFailure() {
610 const bool statusResult = stalledDataOutCswCase(1, 0);
611 const bool residueResult = stalledDataOutCswCase(0, 1);
612 const bool passed = statusResult && residueResult;
614 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-data-out-stall-csw-failure");
617 "HOSTED-WAIT-TEST: FAIL usb-bot-data-out-stall-csw-failure: recovered OUT STALL "
618 "ignored CSW failure or residue");
623bool stalledCswRetriesOnce() {
624 auto* fixtureStorage =
new BotFixture;
626 BotFixture& fixture = *fixtureStorage;
627 expectWrite(fixture, 0, 1);
628 fixture.hub.expectCsw(-Stall, 0, 0, TagReply::Match, CswSignature, 0);
629 fixture.hub.expectControl(StepKind::ClearIn);
630 fixture.hub.expectCsw(13, 0, 0, TagReply::Match, CswSignature, 0);
632 const bool result = sendWrite(fixture);
633 const bool passed = result && fixture.hub.complete() && fixture.in.bDataToggle;
635 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-csw-stall-retry");
638 "HOSTED-WAIT-TEST: FAIL usb-bot-csw-stall-retry: CSW STALL was not cleared and retried "
644bool secondCswStallResets() {
645 auto* fixtureStorage =
new BotFixture;
647 BotFixture& fixture = *fixtureStorage;
648 expectWrite(fixture, 0, 1);
649 fixture.hub.expectCsw(-Stall, 0, 0, TagReply::Match, CswSignature, 0);
650 fixture.hub.expectControl(StepKind::ClearIn);
651 fixture.hub.expectCsw(-Stall, 0, 0, TagReply::Match, CswSignature, 0);
652 fixture.hub.expectControl(StepKind::Reset);
653 fixture.hub.expectControl(StepKind::ClearIn);
654 fixture.hub.expectControl(StepKind::ClearOut);
656 const bool result = sendWrite(fixture);
657 const bool passed = !result && fixture.hub.complete();
659 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-csw-second-stall-reset");
662 "HOSTED-WAIT-TEST: FAIL usb-bot-csw-second-stall-reset: CSW was retried more than once "
663 "or reset recovery was incomplete");
668bool cswTruthCase(ssize_t length, uint8_t status, uint32_t residue, TagReply tagReply,
669 uint32_t signature,
bool recovery) {
670 auto* fixtureStorage =
new BotFixture;
672 BotFixture& fixture = *fixtureStorage;
673 expectWrite(fixture, 0, 1);
674 fixture.hub.expectCsw(length, status, residue, tagReply, signature, 0);
676 fixture.hub.expectControl(StepKind::Reset);
677 fixture.hub.expectControl(StepKind::ClearIn);
678 fixture.hub.expectControl(StepKind::ClearOut);
680 return !sendWrite(fixture) && fixture.hub.complete();
684 const bool length = cswTruthCase(12, 0, 0, TagReply::Match, CswSignature,
true);
685 const bool signature =
686 cswTruthCase(13, 0, 0, TagReply::Match, HOST_TO_LITTLE32(0x12345678),
true);
687 const bool tag = cswTruthCase(13, 0, 0, TagReply::Wrong, CswSignature,
true);
688 const bool passedResidue = cswTruthCase(13, 0, 1, TagReply::Match, CswSignature,
false);
689 const bool failedStatus = cswTruthCase(13, 1, 0, TagReply::Match, CswSignature,
false);
690 const bool phaseError = cswTruthCase(13, 2, 0, TagReply::Match, CswSignature,
true);
691 const bool invalidStatus = cswTruthCase(13, 3, 0, TagReply::Match, CswSignature,
true);
692 const bool excessResidue =
693 cswTruthCase(13, 0, DataBytes + 1, TagReply::Match, CswSignature,
true);
694 const bool passed = length && signature && tag && passedResidue && failedStatus && phaseError &&
695 invalidStatus && excessResidue;
697 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-csw-truth");
700 "HOSTED-WAIT-TEST: FAIL usb-bot-csw-truth: invalid CSW length/signature/tag/status/residue "
701 "was accepted or mis-recovered");
706bool failedRecoveryLatches() {
707 auto* fixtureStorage =
new BotFixture;
709 BotFixture& fixture = *fixtureStorage;
710 expectWrite(fixture, 0, 1);
711 fixture.hub.expectCsw(13, 0, 0, TagReply::Wrong, CswSignature, 0);
712 fixture.hub.expectControl(StepKind::Reset);
713 fixture.hub.expectControl(StepKind::ClearIn, -TransactionError);
714 fixture.hub.expectControl(StepKind::ClearOut);
716 fixture.hub.expectControl(StepKind::Reset);
717 fixture.hub.expectControl(StepKind::ClearIn);
718 fixture.hub.expectControl(StepKind::ClearOut);
719 expectWrite(fixture, 0, 1);
720 fixture.hub.expectCsw(13, 0, 0, TagReply::Match, CswSignature, 0);
722 const bool first = sendWrite(fixture);
723 const bool second = sendWrite(fixture);
724 const bool passed = !first && second && fixture.hub.complete() && fixture.hub.tagsMonotonic();
726 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-recovery-latch");
729 "HOSTED-WAIT-TEST: FAIL usb-bot-recovery-latch: failed recovery did not block the next CBW "
730 "until all three steps succeeded");
735bool commandBoundsDoNotReachUsb() {
736 alignas(16) uint8_t command[17] = {};
737 for (
size_t i = 0; i <
sizeof(command); ++i)
738 command[i] =
static_cast<uint8_t
>(0xa0 + i);
740 auto* fixtureStorage =
new BotFixture;
742 BotFixture& fixture = *fixtureStorage;
743 UsbMassStorageBotTestAccess::setUnits(fixture.device, 17);
744 const bool unencodableLun =
745 fixture.device.sendCommand(16,
reinterpret_cast<uintptr_t
>(command), 16,
746 reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
true);
747 const bool wideLunRejectedWithoutIo = !unencodableLun && fixture.hub.transactionCount() == 0;
748 UsbMassStorageBotTestAccess::setUnits(fixture.device, 4);
750 const bool zeroSize =
751 fixture.device.sendCommand(3,
reinterpret_cast<uintptr_t
>(command), 0,
752 reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
true);
753 const bool nullCommand = fixture.device.sendCommand(
754 3, 0, 16,
reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
true);
755 const bool oversized =
756 fixture.device.sendCommand(3,
reinterpret_cast<uintptr_t
>(command),
sizeof(command),
757 reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
true);
758 const bool unavailableLun =
759 fixture.device.sendCommand(4,
reinterpret_cast<uintptr_t
>(command), 16,
760 reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
true);
761 const bool nullPayload =
762 fixture.device.sendCommand(3,
reinterpret_cast<uintptr_t
>(command), 16, 0, DataBytes,
true);
763 const bool rejectedWithoutIo = wideLunRejectedWithoutIo && !zeroSize && !nullCommand &&
764 !oversized && !unavailableLun && !nullPayload &&
765 fixture.hub.transactionCount() == 0;
767 fixture.hub.expectCbw(3, command, 16, DataBytes, 0);
768 fixture.hub.expectDataOut(fixture.payload, DataBytes, 1, DataBytes);
769 fixture.hub.expectCsw(13, 0, 0, TagReply::Match, CswSignature, 0);
771 fixture.device.sendCommand(3,
reinterpret_cast<uintptr_t
>(command), 16,
772 reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
true);
773 const bool complete = fixture.hub.complete();
774 const bool firstTag = fixture.hub.firstTagIs(1);
775 const bool passed = rejectedWithoutIo && valid && complete && firstTag;
777 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-cdb-bounds");
780 "HOSTED-WAIT-TEST: FAIL usb-bot-cdb-bounds: invalid CDB/LUN/payload reached USB or "
781 "consumed the first tag; rejected="
782 << rejectedWithoutIo <<
", valid=" << valid <<
", complete=" << complete
783 <<
", first-tag=" << firstTag);
787bool dataInCase(ssize_t dataResult, ssize_t cswBytes, TagReply tag,
bool recovery,
bool expected,
788 int cswFirstToggle) {
789 auto* fixtureStorage =
new BotFixture;
791 BotFixture& fixture = *fixtureStorage;
792 alignas(16) uint8_t reference[DataBytes];
793 for (
size_t i = 0; i < DataBytes; ++i)
794 reference[i] =
static_cast<uint8_t
>(i * 13);
795 fixture.hub.expectCbw(3, WriteCdb,
sizeof(WriteCdb), DataBytes, 0, 31,
false);
796 fixture.hub.expectDataIn(reference, DataBytes, dataResult);
797 if (dataResult == -Stall)
798 fixture.hub.expectControl(StepKind::ClearIn);
799 if (dataResult >= 0 || dataResult == -Stall)
800 fixture.hub.expectCsw(cswBytes, 0, 0, tag, CswSignature, cswFirstToggle);
802 fixture.hub.expectControl(StepKind::Reset);
803 fixture.hub.expectControl(StepKind::ClearIn);
804 fixture.hub.expectControl(StepKind::ClearOut);
807 fixture.device.sendCommand(3,
reinterpret_cast<uintptr_t
>(WriteCdb),
sizeof(WriteCdb),
808 reinterpret_cast<uintptr_t
>(fixture.payload), DataBytes,
false);
809 return result == expected && fixture.hub.complete() &&
810 (!expected || !MemoryCompare(reference, fixture.payload, DataBytes));
812bool dataInAndNoData() {
813 bool passed = dataInCase(DataBytes, 13, TagReply::Match,
false,
true, 0);
814 passed &= dataInCase(DataBytes / 2, 13, TagReply::Match,
false,
false, 1);
815 passed &= dataInCase(-Stall, 13, TagReply::Match,
false,
false, 0);
816 passed &= dataInCase(-TransactionError, 13, TagReply::Match,
true,
false, 0);
817 passed &= dataInCase(DataBytes, 12, TagReply::Match,
true,
false, 0);
818 passed &= dataInCase(DataBytes, 13, TagReply::Wrong,
true,
false, 0);
819 auto* fixtureStorage =
new BotFixture;
821 BotFixture& fixture = *fixtureStorage;
822 fixture.hub.expectCbw(3, WriteCdb,
sizeof(WriteCdb), 0, 0);
823 fixture.hub.expectCsw(13, 0, 0);
824 passed &= fixture.device.sendCommand(3,
reinterpret_cast<uintptr_t
>(WriteCdb),
sizeof(WriteCdb),
826 fixture.hub.complete();
828 NOTICE(
"HOSTED-WAIT-TEST: PASS usb-bot-data-in-and-no-data");
830 ERROR(
"HOSTED-WAIT-TEST: FAIL usb-bot-data-in-and-no-data");
836EXPORTED_PUBLIC
bool runHostedUsbBotRegressions() {
837 const bool reads = dataInAndNoData();
838 const bool complete = completeDataOut();
839 const bool cbwExact = invalidCbwResets();
840 const bool shortReset = shortDataOutResets();
841 const bool stalledData = stalledDataOutUsesCsw();
842 const bool stalledDataFailure = stalledDataOutHonoursCswFailure();
843 const bool stalledCsw = stalledCswRetriesOnce();
844 const bool secondCswStall = secondCswStallResets();
845 const bool truth = cswTruth();
846 const bool recoveryLatch = failedRecoveryLatches();
847 const bool commandBounds = commandBoundsDoNotReachUsb();
848 return reads && complete && cbwExact && shortReset && stalledData && stalledDataFailure &&
849 stalledCsw && secondCswStall && truth && recoveryLatch && commandBounds;
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