From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Devices/USB/testcase/Makefile.kup | 0 .../Devices/USB/testcase/tstOhciRegisterAccess.cpp | 604 +++++++++++++++++++++ src/VBox/Devices/USB/testcase/tstPalmOne.c | 412 ++++++++++++++ src/VBox/Devices/USB/testcase/tstTrekStorGo.c | 323 +++++++++++ 4 files changed, 1339 insertions(+) create mode 100644 src/VBox/Devices/USB/testcase/Makefile.kup create mode 100644 src/VBox/Devices/USB/testcase/tstOhciRegisterAccess.cpp create mode 100644 src/VBox/Devices/USB/testcase/tstPalmOne.c create mode 100644 src/VBox/Devices/USB/testcase/tstTrekStorGo.c (limited to 'src/VBox/Devices/USB/testcase') diff --git a/src/VBox/Devices/USB/testcase/Makefile.kup b/src/VBox/Devices/USB/testcase/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/Devices/USB/testcase/tstOhciRegisterAccess.cpp b/src/VBox/Devices/USB/testcase/tstOhciRegisterAccess.cpp new file mode 100644 index 00000000..47412438 --- /dev/null +++ b/src/VBox/Devices/USB/testcase/tstOhciRegisterAccess.cpp @@ -0,0 +1,604 @@ +/* $Id: tstOhciRegisterAccess.cpp $ */ +/** @file + * tstOhciRegisterAccess - OHCI Register Access Tests / Experiments. + */ + +/* + * Copyright (C) 2011-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include +#undef LogRel +#define LogRel(a) SUPR0Printf a + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Register names. */ +static const char * const g_apszRegNms[] = +{ + /* 00 */ "HcRevision", + /* 01 */ "HcControl", + /* 02 */ "HcCommandStatus", + /* 03 */ "HcInterruptStatus", + /* 04 */ "HcInterruptEnable", + /* 05 */ "HcInterruptDisable", + /* 06 */ "HcHCCA", + /* 07 */ "HcPeriodCurrentED", + /* 08 */ "HcControlHeadED", + /* 09 */ "HcControlCurrentED", + /* 10 */ "HcBulkHeadED", + /* 11 */ "HcBulkCurrentED", + /* 12 */ "HcDoneHead", + /* 13 */ "HcFmInterval", + /* 14 */ "HcFmRemaining", + /* 15 */ "HcFmNumber", + /* 16 */ "HcPeriodicStart", + /* 17 */ "HcLSThreshold", + /* 18 */ "HcRhDescriptorA", + /* 19 */ "HcRhDescriptorB", + /* 20 */ "HcRhStatus", + /* Variable number of root hub ports: */ + /* 21 */ "HcRhPortStatus[0]", + /* 22 */ "HcRhPortStatus[1]", + /* 23 */ "HcRhPortStatus[2]", + /* 24 */ "HcRhPortStatus[3]", + /* 25 */ "HcRhPortStatus[4]", + /* 26 */ "HcRhPortStatus[5]", + /* 27 */ "HcRhPortStatus[6]", + /* 28 */ "HcRhPortStatus[7]" +}; + + +static bool TestOhciWrites(RTVPTRUNION uPtr) +{ + static struct + { + unsigned iReg; + uint32_t fMask; + uint32_t uVal1; + uint32_t uVal2; + } const s_aRegs[] = + { +#if 0 /* deadly when missing bytes are taken as zero. */ + { 13 /* HcFmInterval */, 0xffffffff, 0x58871120, 0x01010101 }, +#endif + { 16 /* HcPeriodicStart */, 0x00003fff, 0x01020304, 0x02010403 }, + { 17 /* HcLSThreshold */, 0x00000fff, 0xffffffff, 0x66666666 }, + { 10 /* HcBulkHeadED */, 0xfffffff0, 0xffffffff, 0xfefefef8 }, /* a bit risky... */ + { 11 /* HcBulkCurrentED */, 0xfffffff0, 0xffffffff, 0xfefefef8 }, /* a bit risky... */ + }; + + bool fSuccess = true; + for (unsigned i = 0; i < RT_ELEMENTS(s_aRegs); i++) + { + uint32_t const iReg = s_aRegs[i].iReg; + RTVPTRUNION uPtrReg; + uPtrReg.pu32 = &uPtr.pu32[iReg]; + + uint32_t uInitialValue = *uPtrReg.pu32; + LogRel(("TestOhciWrites: %p iReg=%2d %20s = %08RX32\n", uPtrReg.pv, iReg, g_apszRegNms[iReg], uInitialValue)); + + bool fTryAgain = true; + const char *pszError = NULL; + uint32_t u32A = 0; + uint32_t uChangedValue = 0; + uint32_t uExpectedValue = 0; + + for (uint32_t iTry = 0; fTryAgain && iTry < 1024; iTry++) + { + pszError = NULL; + fTryAgain = false; + u32A = 0; + uChangedValue = 0; + uExpectedValue = 0; + + RTCCUINTREG const fFlags = ASMIntDisableFlags(); + uInitialValue = *uPtrReg.pu32; + + /* + * DWORD writes. + */ + if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue))) + break; + *uPtrReg.pu32 = uInitialValue; + u32A = *uPtrReg.pu32; + uChangedValue = s_aRegs[i].uVal1 != uInitialValue ? s_aRegs[i].uVal1 : s_aRegs[i].uVal2; + if (u32A == uInitialValue) + { + /* Change the value. */ + *uPtrReg.pu32 = uChangedValue; + u32A = *uPtrReg.pu32; + *uPtrReg.pu32 = uInitialValue; + uExpectedValue = uChangedValue & s_aRegs[i].fMask; + if (u32A != uExpectedValue) + pszError = "Writing changed value failed"; + else + { + u32A = *uPtrReg.pu32; + if (u32A != uInitialValue) + pszError = "Restore error 1"; + } + } + else + pszError = "Writing back initial value failed"; + + /* + * Write aligned word changes. + */ + for (unsigned iWord = 0; iWord < 2 && !pszError && !fTryAgain; iWord++) + { + if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue))) + break; + + /* Change the value. */ + uPtrReg.pu16[iWord] = (uint16_t)(uChangedValue >> iWord * 16); + u32A = *uPtrReg.pu32; + *uPtrReg.pu32 = uInitialValue; + uExpectedValue = (uChangedValue & UINT32_C(0xffff) << iWord * 16) & s_aRegs[i].fMask; + if (u32A != uExpectedValue) + { + static const char * const s_apsz[] = { "word 0", "word 1" }; + pszError = s_apsz[iWord]; + } + else + { + u32A = *uPtrReg.pu32; + if (u32A != uInitialValue) + pszError = "Restore error 2"; + } + } + + /* + * Write aligned word change. We have to keep within the register, + * unfortunately. + */ + if (!pszError && !fTryAgain) + { + fTryAgain = *uPtrReg.pu32 != uInitialValue; + if (!fTryAgain) + { + /* Change the value. */ + *(uint16_t volatile *)&uPtrReg.pu8[1] = (uint16_t)(uChangedValue >> 8); + u32A = *uPtrReg.pu32; + *uPtrReg.pu32 = uInitialValue; + uExpectedValue = (uChangedValue & UINT32_C(0x00ffff00)) & s_aRegs[i].fMask; + if (u32A != uExpectedValue) + pszError = "Unaligned word access"; + else + { + u32A = *uPtrReg.pu32; + if (u32A != uInitialValue) + pszError = "Restore error 3"; + } + } + } + + /* + * Write byte changes. + */ + for (unsigned iByte = 0; iByte < 4 && !pszError && !fTryAgain; iByte++) + { + if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue))) + break; + + /* Change the value. */ + uPtrReg.pu8[iByte] = (uint8_t)(uChangedValue >> iByte * 8); + u32A = *uPtrReg.pu32; + *uPtrReg.pu32 = uInitialValue; + uExpectedValue = (uChangedValue & UINT32_C(0xff) << iByte * 8) & s_aRegs[i].fMask; + if (u32A != uExpectedValue) + { + static const char * const s_apsz[] = { "byte 0", "byte 1", "byte 2", "byte 3" }; + pszError = s_apsz[iByte]; + } + else + { + u32A = *uPtrReg.pu32; + if (u32A != uInitialValue) + pszError = "Restore error 4"; + } + } + + ASMSetFlags(fFlags); + ASMNopPause(); + } + + /* + * Complain on failure. + */ + if (fTryAgain) + LogRel(("TestOhciWrites: Warning! Register %s was never stable enough for testing! %08RX32 %08RX32 %08RX32\n", + g_apszRegNms[iReg], uInitialValue, u32A, uChangedValue, uInitialValue)); + else if (pszError) + { + LogRel(("TestOhciWrites: Error! Register %s failed: %s; Initial=%08RX32 Changed=%08RX32 Expected=%08RX32 u32A=%08RX32\n", + g_apszRegNms[iReg], pszError, uInitialValue, uChangedValue, uExpectedValue, u32A)); + fSuccess = false; + } + } + + return fSuccess; +} + + +static bool TestOhciReadOnly(RTVPTRUNION uPtr) +{ + static struct + { + unsigned iReg; + uint32_t cValues; + uint32_t auValues[8]; + } const s_aRegs[] = + { + { 0 /* HcRevision */, 8, { 0, UINT32_MAX, 0x10100110, 0x200, 0x111, 0x11f, 0xf110, 0x0f10 } }, + { 12 /* HcDoneHead */, 3, { 0, UINT32_MAX, 0x55555555, 0, 0, 0, 0, 0 } }, + { 14 /* HcFmRemaining */, 3, { 0, UINT32_MAX, 0x55555555, 0, 0, 0, 0, 0 } }, + { 15 /* HcFmNumber */, 5, { 0, UINT32_MAX, 0x55555555, 0x7899, 0x00012222, 0, 0, 0 } }, +#if 0 /* HCD can write this */ + { 17 /* HcLSThreshold */, 5, { 0x627, 0x628, 0x629, 0x666, 0x599, 0, 0, 0 } } /* ??? */ +#endif + }; + + bool fSuccess = true; + for (unsigned i = 0; i < RT_ELEMENTS(s_aRegs); i++) + { + uint32_t const iReg = s_aRegs[i].iReg; + RTVPTRUNION uPtrReg; + uPtrReg.pu32 = &uPtr.pu32[iReg]; + + uint32_t uInitialValue = *uPtrReg.pu32; + LogRel(("TestOhciReadOnly: %p iReg=%2d %20s = %08RX32\n", uPtrReg.pv, iReg, g_apszRegNms[iReg], uInitialValue)); + + bool fTryAgain = true; + const char *pszError = NULL; + uint32_t uChangedValue = 0; + uint32_t u32A = 0; + + for (uint32_t iTry = 0; fTryAgain && iTry < 1024; iTry++) + { + pszError = NULL; + fTryAgain = false; + u32A = 0; + uChangedValue = 0; + + RTCCUINTREG const fFlags = ASMIntDisableFlags(); + uInitialValue = *uPtrReg.pu32; + + /* + * Try aligned dword, word and byte writes for now. + */ + for (unsigned iValue = 0; iValue < s_aRegs[i].cValues && !pszError && !fTryAgain; iValue++) + { + uChangedValue = s_aRegs[i].auValues[iValue]; + if (uInitialValue == uChangedValue) + continue; + + /* dword */ + if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue))) + break; + + *uPtrReg.pu32 = uChangedValue; + u32A = *uPtrReg.pu32; + *uPtrReg.pu32 = uInitialValue; + if (u32A != uInitialValue) + pszError = "dword access"; + else + { + u32A = *uPtrReg.pu32; + if (u32A != uInitialValue) + pszError = "Restore error 1"; + } + + /* word */ + for (unsigned iWord = 0; iWord < 2 && !pszError && !fTryAgain; iWord++) + { + if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue))) + break; + uPtrReg.pu16[iWord] = (uint16_t)(uChangedValue >> iWord * 16); + u32A = *uPtrReg.pu32; + *uPtrReg.pu32 = uInitialValue; + if (u32A != uInitialValue) + pszError = iWord == 0 ? "aligned word 0 access" : "aligned word 1 access"; + else + { + u32A = *uPtrReg.pu32; + if (u32A != uInitialValue) + pszError = "Restore error 2"; + } + } + + /* byte */ + for (unsigned iByte = 0; iByte < 4 && !pszError && !fTryAgain; iByte++) + { + if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue))) + break; + uPtrReg.pu8[iByte] = (uint8_t)(uChangedValue >> iByte * 8); + u32A = *uPtrReg.pu32; + *uPtrReg.pu32 = uInitialValue; + if (u32A != uInitialValue) + { + static const char * const s_apsz[] = { "byte 0", "byte 1", "byte 2", "byte 3" }; + pszError = s_apsz[iByte]; + } + else + { + u32A = *uPtrReg.pu32; + if (u32A != uInitialValue) + pszError = "Restore error 3"; + } + } + } + + ASMSetFlags(fFlags); + ASMNopPause(); + } + + /* + * Complain on failure. + */ + if (fTryAgain) + LogRel(("TestOhciReadOnly: Warning! Register %s was never stable enough for testing! %08RX32 %08RX32 %08RX32\n", + g_apszRegNms[iReg], uInitialValue, u32A, uChangedValue, uInitialValue)); + else if (pszError) + { + LogRel(("TestOhciReadOnly: Error! Register %s failed: %s; uInitialValue=%08RX32 uChangedValue=%08RX32 u32A=%08RX32\n", + g_apszRegNms[iReg], pszError, uInitialValue, uChangedValue, u32A)); + fSuccess = false; + } + } + + return fSuccess; +} + + +static bool TestOhciReads(RTVPTRUNION uPtr) +{ + /* + * We can read just about any register we like since read shouldn't have + * any side effects. However, some registers are volatile and makes for + * difficult targets, thus the ugly code. + */ + bool fSuccess = true; + uint32_t cMaxReg = RT_ELEMENTS(g_apszRegNms); + for (uint32_t iReg = 0; iReg < cMaxReg; iReg++, uPtr.pu32++) + { + const char *pszError = NULL; + bool fDone = false; + uint32_t uInitialValue = *uPtr.pu32; + uint32_t u32A = 0; + uint32_t u32B = 0; + uint32_t u32C = 0; + LogRel(("TestOhciReads: %p iReg=%2d %20s = %08RX32\n", uPtr.pv, iReg, g_apszRegNms[iReg], uInitialValue)); + + for (uint32_t iTry = 0; !fDone && iTry < 1024; iTry++) + { + pszError = NULL; + fDone = true; + u32A = u32B = u32C = 0; + + RTCCUINTREG const fFlags = ASMIntDisableFlags(); + uInitialValue = *uPtr.pu32; + + /* Test byte access. */ + for (unsigned iByte = 0; iByte < 4; iByte++) + { + u32A = *uPtr.pu32; + u32B = uPtr.pu8[iByte]; + u32C = *uPtr.pu32; + if (u32A != uInitialValue || u32C != uInitialValue) + { + fDone = false; + break; + } + + static uint32_t const a_au32Masks[] = + { + UINT32_C(0xffffff00), UINT32_C(0xffff00ff), UINT32_C(0xff00ffff), UINT32_C(0x00ffffff) + }; + u32B <<= iByte * 8; + u32B |= uInitialValue & a_au32Masks[iByte]; + if (u32B != uInitialValue) + { + static const char * const s_apsz[] = { "byte 0", "byte 1", "byte 2", "byte 3" }; + pszError = s_apsz[iByte]; + break; + } + } + + /* Test aligned word access. */ + if (fDone) + { + for (unsigned iWord = 0; iWord < 2; iWord++) + { + u32A = *uPtr.pu32; + u32B = uPtr.pu16[iWord]; + u32C = *uPtr.pu32; + if (u32A != uInitialValue || u32C != uInitialValue) + { + fDone = false; + break; + } + + u32B <<= iWord * 16; + u32B |= uInitialValue & (iWord == 0 ? UINT32_C(0xffff0000) : UINT32_C(0x0000ffff)); + if (u32B != uInitialValue) + { + pszError = iWord == 0 ? "aligned word 0 access" : "aligned word 1 access"; + break; + } + } + } + + /* Test unaligned word access. */ + if (fDone) + { + for (int iWord = (uPtr.u & HOST_PAGE_OFFSET_MASK) == 0; iWord < 3; iWord++) + { + u32A = *uPtr.pu32; + u32B = *(volatile uint16_t *)&uPtr.pu8[iWord * 2 - 1]; + u32C = *uPtr.pu32; + if (u32A != uInitialValue || u32C != uInitialValue) + { + fDone = false; + break; + } + + switch (iWord) + { + case 0: u32B = (u32B >> 8) | (u32A & UINT32_C(0xffffff00)); break; + case 1: u32B = (u32B << 8) | (u32A & UINT32_C(0xff0000ff)); break; + case 2: u32B = (u32B << 24) | (u32A & UINT32_C(0x00ffffff)); break; + } + if (u32B != u32A) + { + static const char * const s_apsz[] = { "unaligned word 0", "unaligned word 1", "unaligned word 2" }; + pszError = s_apsz[iWord]; + break; + } + } + } + + /* Test unaligned dword access. */ + if (fDone) + { + for (int iByte = (uPtr.u & HOST_PAGE_OFFSET_MASK) == 0 ? 0 : -3; iByte < 4; iByte++) + { + u32A = *uPtr.pu32; + u32B = *(volatile uint32_t *)&uPtr.pu8[iByte]; + u32C = *uPtr.pu32; + if (u32A != uInitialValue || u32C != uInitialValue) + { + fDone = false; + break; + } + + switch (iByte) + { + case -3: u32B = (u32B >> 24) | (uInitialValue & UINT32_C(0xffffff00)); break; + case -2: u32B = (u32B >> 16) | (uInitialValue & UINT32_C(0xffff0000)); break; + case -1: u32B = (u32B >> 8) | (uInitialValue & UINT32_C(0xff000000)); break; + case 0: break; + case 1: u32B = (u32B << 8) | (uInitialValue & UINT32_C(0x000000ff)); break; + case 2: u32B = (u32B << 16) | (uInitialValue & UINT32_C(0x0000ffff)); break; + case 3: u32B = (u32B << 24) | (uInitialValue & UINT32_C(0x00ffffff)); break; + + } + if (u32B != u32A) + { + static const char * const s_apsz[] = + { + "unaligned dword -3", "unaligned dword -2", "unaligned dword -1", + "unaligned dword 0", "unaligned dword 1", "unaligned dword 2", "unaligned dword 3" + }; + pszError = s_apsz[iByte + 3]; + break; + } + + } + } + + ASMSetFlags(fFlags); + ASMNopPause(); + } /* try loop */ + + /* + * Complain on failure. + */ + if (!fDone) + LogRel(("TestOhciReads: Warning! Register %s was never stable enough for testing! %08RX32 %08RX32 %08RX32\n", + g_apszRegNms[iReg], uInitialValue, u32A, u32C)); + else if (pszError) + { + LogRel(("TestOhciReads: Error! Register %s failed: %s; uInitialValue=%08RX32 u32B=%08RX32\n", + g_apszRegNms[iReg], pszError, uInitialValue, u32B)); + fSuccess = false; + } + } + + return fSuccess; +} + + +int tstOhciRegisterAccess(RTHCPHYS HCPhysOHCI) +{ + LogRel(("tstOhciRegisterAccess: HCPhysOHCI=%RHp\n", HCPhysOHCI)); + + /* + * Map the OHCI registers so we can access them. + */ + RTR0MEMOBJ hMemObj; + int rc = RTR0MemObjEnterPhys(&hMemObj, HCPhysOHCI, HOST_PAGE_SIZE, RTMEM_CACHE_POLICY_MMIO); + if (RT_FAILURE(rc)) + { + LogRel(("tstOhciRegisterAccess: Failed to enter OHCI memory at %RHp: %Rrc\n", HCPhysOHCI, rc)); + return rc; + } + RTR0MEMOBJ hMapObj; + rc = RTR0MemObjMapKernel(&hMapObj, hMemObj, (void *)-1, 0 /*uAlignment*/, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + if (RT_SUCCESS(rc)) + { + RTVPTRUNION uPtr; + uPtr.pv = (void volatile *)RTR0MemObjAddress(hMapObj); + LogRel(("tstOhciRegisterAccess: mapping address %p\n", uPtr.pv)); + if (RT_VALID_PTR(uPtr.pv)) + { + LogRel(("tstOhciRegisterAccess: HcRevision=%#x\n", *uPtr.pu32)); + + /* + * Do the access tests. + */ + bool fSuccess = TestOhciReads(uPtr); + if (fSuccess) + fSuccess = TestOhciReadOnly(uPtr); + if (fSuccess) + fSuccess = TestOhciWrites(uPtr); + if (fSuccess) + LogRel(("tstOhciRegisterAccess: Success!\n")); + else + LogRel(("tstOhciRegisterAccess: Failed!\n")); + } + else + rc = VERR_INTERNAL_ERROR_2; + + /* + * Clean up. + */ + RTR0MemObjFree(hMapObj, false); + } + else + LogRel(("tstOhciRegisterAccess: Failed to map OHCI memory at %RHp: %Rrc\n", HCPhysOHCI, rc)); + RTR0MemObjFree(hMemObj, false); + LogRel(("tstOhciRegisterAccess: returns %Rrc\n", rc)); + return rc; +} diff --git a/src/VBox/Devices/USB/testcase/tstPalmOne.c b/src/VBox/Devices/USB/testcase/tstPalmOne.c new file mode 100644 index 00000000..80c605bb --- /dev/null +++ b/src/VBox/Devices/USB/testcase/tstPalmOne.c @@ -0,0 +1,412 @@ +/* $Id: tstPalmOne.c $ */ +/** @file + * USB PalmOne testcase + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** @name USB Control message recipient codes (from spec) + * @{ */ +#define VUSB_TO_DEVICE 0x0 +#define VUSB_TO_INTERFACE 0x1 +#define VUSB_TO_ENDPOINT 0x2 +#define VUSB_TO_OTHER 0x3 +#define VUSB_RECIP_MASK 0x1f +/** @} */ + +/** @name USB control pipe setup packet structure (from spec) + * @{ */ +#define VUSB_REQ_SHIFT (5) +#define VUSB_REQ_STANDARD (0x0 << VUSB_REQ_SHIFT) +#define VUSB_REQ_CLASS (0x1 << VUSB_REQ_SHIFT) +#define VUSB_REQ_VENDOR (0x2 << VUSB_REQ_SHIFT) +#define VUSB_REQ_RESERVED (0x3 << VUSB_REQ_SHIFT) +#define VUSB_REQ_MASK (0x3 << VUSB_REQ_SHIFT) +/** @} */ + +#define VUSB_DIR_TO_HOST 0x80 +typedef struct vusb_setup +{ + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} VUSBSETUP, *PVUSBSETUP; +typedef const VUSBSETUP *PCVUSBSETUP; + + +int g_fd; + +int bitch(const char *pszMsg) +{ + printf("failure: %s: %d %s\n", pszMsg, errno, strerror(errno)); + return 1; +} + +void hex(const void *pv, ssize_t cb, const char *pszWhat) +{ + printf("%s: cb=%d\n", pszWhat, cb); + unsigned char *pb = (unsigned char *)pv; + int cch = 0; + int off = 0; + int cchPrecision = 16; + while (off < cb) + { + int i; + printf("%s%0*x %04x:", off ? "\n" : "", sizeof(pb) * 2, (uintptr_t)pb, off); + + for (i = 0; i < cchPrecision && off + i < cb; i++) + printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]); + while (i++ < cchPrecision) + printf(" "); + printf(" "); + for (i = 0; i < cchPrecision && off + i < cb; i++) + { + uint8_t u8 = pb[i]; + fputc(u8 < 127 && u8 >= 32 ? u8 : '.', stdout); + } + + /* next */ + pb += cchPrecision; + off += cchPrecision; + } + printf("\n"); +} + +int doioctl(int iCmd, void *pvData, const char *pszWho) +{ + int rc; + do + { + errno = 0; + rc = ioctl(g_fd, iCmd, pvData); + + } while (rc && errno == EAGAIN); + if (rc) + printf("doioctl: %s: iCmd=%#x errno=%d %s\n", pszWho, iCmd, errno, strerror(errno)); + else + printf("doioctl: %s: iCmd=%#x ok\n", pszWho, iCmd); + return rc; +} + +int dobulk(int EndPt, void *pvBuf, size_t cbBuf, const char *pszWho) +{ +#if 0 + struct usbdevfs_urb KUrb = {0}; + KUrb.type = USBDEVFS_URB_TYPE_BULK; + KUrb.endpoint = EndPt; + KUrb.buffer = pvBuf; + KUrb.buffer_length = cbBuf; + KUrb.actual_length = 0; //cbBuf + KUrb.flags = 0; /* ISO_ASAP/SHORT_NOT_OK */ + if (!doioctl(USBDEVFS_SUBMITURB, &KUrb, pszWho)) + { + struct usbdevfs_urb *pKUrb = NULL; + if (!doioctl(USBDEVFS_REAPURB, &pKUrb, pszWho) + && pKUrb == &KUrb) + return KUrb.actual_length; + } + return -1; +#else + struct usbdevfs_bulktransfer BulkMsg = {0}; + + BulkMsg.ep = EndPt; + BulkMsg.timeout = 1000; + BulkMsg.len = cbBuf; + BulkMsg.data = pvBuf; + int rc = doioctl(USBDEVFS_BULK, &BulkMsg, pszWho); +// printf("rc=%d BulkMsg.len=%d cbBuf=%d\n", rc, BulkMsg.len, cbBuf); + if (rc >= 0) + return rc; + return -1; +#endif +} + +int send_bulk(int EndPt, void *pvBuf, size_t cbBuf) +{ + return dobulk(EndPt, pvBuf, cbBuf, "send_bulk"); +} + +int recv_bulk(int EndPt, void *pvBuf, size_t cbBuf) +{ + int cb = dobulk(EndPt | 0x80, pvBuf, cbBuf, "recv_bulk"); + if (cb > 0) + printf("cb=%d\n", cb); + return cb; +} + +int doctrl(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, + void *pvBuf, const char *pszWho) +{ +#if 0 + struct usbdevfs_urb KUrb = {0}; + KUrb.type = USBDEVFS_URB_TYPE_BULK; + KUrb.endpoint = EndPt; + KUrb.buffer = pvBuf; + KUrb.buffer_length = cbBuf; + KUrb.actual_length = 0; //cbBuf + KUrb.flags = 0; /* ISO_ASAP/SHORT_NOT_OK */ + if (!doioctl(USBDEVFS_SUBMITURB, &KUrb, pszWho)) + { + struct usbdevfs_urb *pKUrb = NULL; + if (!doioctl(USBDEVFS_REAPURB, &pKUrb, pszWho) + && pKUrb == &KUrb) + return KUrb.actual_length; + } + return -1; +#else + struct usbdevfs_ctrltransfer CtrlMsg = {0}; + + CtrlMsg.bRequestType = bmRequestType; + CtrlMsg.bRequest = bRequest; + CtrlMsg.wValue = wValue; + CtrlMsg.wLength = wLength; + CtrlMsg.timeout = 1000; + CtrlMsg.data = pvBuf; + + int rc = doioctl(USBDEVFS_CONTROL, &CtrlMsg, pszWho); + printf("rc=%d CtrlMsg.wLength=%d\n", rc, CtrlMsg.wLength); + if (rc >= 0) + return rc; + return -1; +#endif +} + +static int claim_if(int iIf) +{ + return doioctl(USBDEVFS_CLAIMINTERFACE, &iIf, "claim_if"); +} + +static int usb_set_connected(int ifnum, int conn) +{ + struct usbdevfs_ioctl io; + io.ifno = ifnum; + io.ioctl_code = (conn) ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT; + io.data = NULL; + return doioctl(USBDEVFS_IOCTL, &io, "set_connected"); +} + +static int set_config(int iCfg) +{ + return doioctl(USBDEVFS_SETCONFIGURATION, &iCfg, "set_config"); +} + +static int set_interface(int iIf, int iAlt) +{ + struct usbdevfs_setinterface SetIf = {0}; + SetIf.interface = iIf; + SetIf.altsetting = iAlt; + return doioctl(USBDEVFS_SETINTERFACE, &SetIf, "set_interface"); +} + +/* can be exploited to check if there is an active config. */ +static int reset_ep(int EndPt) +{ + return doioctl(USBDEVFS_RESETEP, &EndPt, "reset_ep"); +} + + +static void msd() +{ +#if 1 + unsigned InEndPt = 1; + unsigned OutEndPt = 1; +#else + unsigned InEndPt = 1; + unsigned OutEndPt = 2; +#endif + unsigned char abBuf[512]; + int i; + +// set_config(1); - the culprit + set_interface(0, 0); + +#if 0 + /* Send an Get Max LUN request */ + abBuf[0] = 0; + if (doctrl(VUSB_DIR_TO_HOST | VUSB_REQ_CLASS | VUSB_TO_INTERFACE, + 0xfe /* max lun */, 0, 1, 1, abBuf, "get max lun") >= 0) + printf("max luns: %d\n", abBuf[0]); +#endif + + for (i = 0; i < 3; i++) + { + printf("i=%d\n", i); + + /* Send an INQUIRY command to ep 2 */ + memset(abBuf, 0, sizeof(abBuf)); + memcpy(abBuf, "USBC", 4); + *(uint32_t *)(&abBuf[4]) = 0x12330984 ; + //abBuf[8] = 0x08; + abBuf[8] = 0x24; + abBuf[0xc] = 0x80; + abBuf[0xe] = 0x06; /* cmd length */ + abBuf[0x0f] = 0x12; /* cmd - INQUIRY */ + abBuf[0x13] = 0x24; + + hex(abBuf, 31, "intquery req"); + if (send_bulk(OutEndPt, abBuf, 31) < 0) + return; + //usleep(15000); + + /* read result */ + memset(abBuf, 0, sizeof(abBuf)); + //printf("recv..\n"); + int cb = recv_bulk(InEndPt, abBuf, 36); + hex(abBuf, cb, "inquiry result"); + + /* sense? */ + memset(abBuf, 0, sizeof(abBuf)); + cb = recv_bulk(InEndPt, abBuf, 36); + hex(abBuf, cb, "inquiry sense?"); + usleep(150000); + } +} + +void palm(void) +{ +// set_config(1); //skip this +// reset_ep(6); + + /* This seems to be some kind of 'identify device' request. */ + uint8_t abVendor[0x14] = {0}; + int cb = doctrl(VUSB_DIR_TO_HOST | VUSB_REQ_VENDOR | VUSB_TO_ENDPOINT, + 0x04, 0, 0, 0x14, abVendor, "vendor req"); + hex(abVendor, cb, "palm vendor req"); + + /* read from ep 6. */ + uint8_t abBuf[512]; + memset(abBuf, 0, sizeof(abBuf)); + cb = recv_bulk(6, abBuf, 6); + hex(abBuf, cb, "bulk 1"); + + /* read from ep 6. */ + memset(abBuf, 0, sizeof(abBuf)); + cb = recv_bulk(6, abBuf, 22); + hex(abBuf, cb, "bulk 2"); + +#if 0 + /* write to ep 6 */ + memset(abBuf, 0, sizeof(abBuf)); + abBuf[0] = 1; + abBuf[1] = 2; + abBuf[5] = 0x32; + if (send_bulk(7, abBuf, 6) < 0) + return; + + memset(abBuf, 0, sizeof(abBuf)); + abBuf[0] = 0x12; + abBuf[1] = 1; + if (send_bulk(7, abBuf, 8) < 0) + return; + + memset(abBuf, 0, sizeof(abBuf)); + abBuf[1] = 0x20; + abBuf[6] = 0x24; + if (send_bulk(7, abBuf, 6) < 0) + return; + + memset(abBuf, 0, sizeof(abBuf)); + abBuf[0] = 0xff; + abBuf[1] = 0xff; + abBuf[2] = 0xff; + abBuf[3] = 0xff; + abBuf[4] = 0x3c; + abBuf[6] = 0x3c; + abBuf[0x0f] = 1; + abBuf[0x11] = 0x10; + abBuf[0x15] = 0x10; + abBuf[0x18] = 0x3c; + abBuf[0x1a] = 0x3c; + if (send_bulk(7, abBuf, 0x24) < 0) + return; + + /* read from ep 6 */ + memset(abBuf, 0, sizeof(abBuf)); + cb = recv_bulk(6, abBuf, 64); + hex(abBuf, cb, "bulk 3"); + + /* read from ep 6. */ + memset(abBuf, 0, sizeof(abBuf)); + cb = recv_bulk(6, abBuf, 50); + hex(abBuf, cb, "bulk 4"); +#endif +} + +int reset(void) +{ + int i = 0; + printf("resetting...\n"); + return doioctl(USBDEVFS_RESET, &i, "reset"); +} + +int main(int argc, char **argv) +{ + g_fd = open(argv[1], O_RDWR); + if (errno == ENOENT && g_fd < 0) + { + int i; + for (i = 0; i < 120; i++) + { + g_fd = open(argv[1], O_RDWR); + if (g_fd >= 0) + break; + printf("."); fflush(stdout); + usleep(500000); + } + printf("\n"); + } + if (g_fd < 0) + return bitch("open"); + +// reset(); +// set_config(0); +// set_config(1); + + usb_set_connected(0, 1); + claim_if(0); + +#if 0 + msd(); +#else + palm(); +#endif + return 0; +} diff --git a/src/VBox/Devices/USB/testcase/tstTrekStorGo.c b/src/VBox/Devices/USB/testcase/tstTrekStorGo.c new file mode 100644 index 00000000..285c49b3 --- /dev/null +++ b/src/VBox/Devices/USB/testcase/tstTrekStorGo.c @@ -0,0 +1,323 @@ +/* $Id: tstTrekStorGo.c $ */ +/** @file + * Some simple inquiry test for the TrekStor USB-Stick GO, linux usbfs + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** @name USB Control message recipient codes (from spec) + * @{ */ +#define VUSB_TO_DEVICE 0x0 +#define VUSB_TO_INTERFACE 0x1 +#define VUSB_TO_ENDPOINT 0x2 +#define VUSB_TO_OTHER 0x3 +#define VUSB_RECIP_MASK 0x1f +/** @} */ + +/** @name USB control pipe setup packet structure (from spec) + * @{ */ +#define VUSB_REQ_SHIFT (5) +#define VUSB_REQ_STANDARD (0x0 << VUSB_REQ_SHIFT) +#define VUSB_REQ_CLASS (0x1 << VUSB_REQ_SHIFT) +#define VUSB_REQ_VENDOR (0x2 << VUSB_REQ_SHIFT) +#define VUSB_REQ_RESERVED (0x3 << VUSB_REQ_SHIFT) +#define VUSB_REQ_MASK (0x3 << VUSB_REQ_SHIFT) +/** @} */ + +#define VUSB_DIR_TO_HOST 0x80 +typedef struct vusb_setup +{ + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} VUSBSETUP, *PVUSBSETUP; +typedef const VUSBSETUP *PCVUSBSETUP; + + +int g_fd; + +int bitch(const char *pszMsg) +{ + printf("failure: %s: %d %s\n", pszMsg, errno, strerror(errno)); + return 1; +} + +void hex(const void *pv, ssize_t cb, const char *pszWhat) +{ + printf("%s: cb=%d\n", pszWhat, cb); + unsigned char *pb = (unsigned char *)pv; + int cch = 0; + int off = 0; + int cchPrecision = 16; + while (off < cb) + { + int i; + printf("%s%0*x %04x:", off ? "\n" : "", sizeof(pb) * 2, (uintptr_t)pb, off); + + for (i = 0; i < cchPrecision && off + i < cb; i++) + printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]); + while (i++ < cchPrecision) + printf(" "); + printf(" "); + for (i = 0; i < cchPrecision && off + i < cb; i++) + { + uint8_t u8 = pb[i]; + fputc(u8 < 127 && u8 >= 32 ? u8 : '.', stdout); + } + + /* next */ + pb += cchPrecision; + off += cchPrecision; + } + printf("\n"); +} + +int doioctl(int iCmd, void *pvData, const char *pszWho) +{ + int rc; + do + { + errno = 0; + rc = ioctl(g_fd, iCmd, pvData); + + } while (rc && errno == EAGAIN); + if (rc) + printf("doioctl: %s: iCmd=%#x errno=%d %s\n", pszWho, iCmd, errno, strerror(errno)); + else + printf("doioctl: %s: iCmd=%#x ok\n", pszWho, iCmd); + return rc; +} + +int dobulk(int EndPt, void *pvBuf, size_t cbBuf, const char *pszWho) +{ +#if 0 + struct usbdevfs_urb KUrb = {0}; + KUrb.type = USBDEVFS_URB_TYPE_BULK; + KUrb.endpoint = EndPt; + KUrb.buffer = pvBuf; + KUrb.buffer_length = cbBuf; + KUrb.actual_length = 0; //cbBuf + KUrb.flags = 0; /* ISO_ASAP/SHORT_NOT_OK */ + if (!doioctl(USBDEVFS_SUBMITURB, &KUrb, pszWho)) + { + struct usbdevfs_urb *pKUrb = NULL; + if (!doioctl(USBDEVFS_REAPURB, &pKUrb, pszWho) + && pKUrb == &KUrb) + return KUrb.actual_length; + } + return -1; +#else + struct usbdevfs_bulktransfer BulkMsg = {0}; + + BulkMsg.ep = EndPt; + BulkMsg.timeout = 1000; + BulkMsg.len = cbBuf; + BulkMsg.data = pvBuf; + int rc = doioctl(USBDEVFS_BULK, &BulkMsg, pszWho); +// printf("rc=%d BulkMsg.len=%d cbBuf=%d\n", rc, BulkMsg.len, cbBuf); + if (rc >= 0) + return rc; + return -1; +#endif +} + +int send_bulk(int EndPt, void *pvBuf, size_t cbBuf) +{ + return dobulk(EndPt, pvBuf, cbBuf, "send_bulk"); +} + +int recv_bulk(int EndPt, void *pvBuf, size_t cbBuf) +{ + int cb = dobulk(EndPt | 0x80, pvBuf, cbBuf, "recv_bulk"); + if (cb > 0) + printf("cb=%d\n", cb); + return cb; +} + +int doctrl(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, + void *pvBuf, const char *pszWho) +{ +#if 0 + struct usbdevfs_urb KUrb = {0}; + KUrb.type = USBDEVFS_URB_TYPE_BULK; + KUrb.endpoint = EndPt; + KUrb.buffer = pvBuf; + KUrb.buffer_length = cbBuf; + KUrb.actual_length = 0; //cbBuf + KUrb.flags = 0; /* ISO_ASAP/SHORT_NOT_OK */ + if (!doioctl(USBDEVFS_SUBMITURB, &KUrb, pszWho)) + { + struct usbdevfs_urb *pKUrb = NULL; + if (!doioctl(USBDEVFS_REAPURB, &pKUrb, pszWho) + && pKUrb == &KUrb) + return KUrb.actual_length; + } + return -1; +#else + struct usbdevfs_ctrltransfer CtrlMsg = {0}; + + CtrlMsg.bRequestType = bmRequestType; + CtrlMsg.bRequest = bRequest; + CtrlMsg.wValue = wValue; + CtrlMsg.wLength = wLength; + CtrlMsg.timeout = 1000; + CtrlMsg.data = pvBuf; + + int rc = doioctl(USBDEVFS_CONTROL, &CtrlMsg, pszWho); + printf("rc=%d CtrlMsg.wLength=%d\n", rc, CtrlMsg.wLength); + if (rc >= 0) + return rc; + return -1; +#endif +} + +static int claim_if(int iIf) +{ + return doioctl(USBDEVFS_CLAIMINTERFACE, &iIf, "claim_if"); +} + +static int usb_set_connected(int ifnum, int conn) +{ + struct usbdevfs_ioctl io; + io.ifno = ifnum; + io.ioctl_code = (conn) ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT; + io.data = NULL; + return doioctl(USBDEVFS_IOCTL, &io, "set_connected"); +} + +static int set_config(int iCfg) +{ + return doioctl(USBDEVFS_SETCONFIGURATION, &iCfg, "set_config"); +} + +static int set_interface(int iIf, int iAlt) +{ + struct usbdevfs_setinterface SetIf = {0}; + SetIf.interface = iIf; + SetIf.altsetting = iAlt; + return doioctl(USBDEVFS_SETINTERFACE, &SetIf, "set_interface"); +} + +/* can be exploited to check if there is an active config. */ +static int reset_ep(int EndPt) +{ + return doioctl(USBDEVFS_RESETEP, &EndPt, "reset_ep"); +} + + +static void msd() +{ +#if 1 + unsigned InEndPt = 1; + unsigned OutEndPt = 1; +#else + unsigned InEndPt = 1; + unsigned OutEndPt = 2; +#endif + unsigned char abBuf[512]; + int i; + +#if 0 + /* Send an Get Max LUN request */ + abBuf[0] = 0; + if (doctrl(VUSB_DIR_TO_HOST | VUSB_REQ_CLASS | VUSB_TO_INTERFACE, + 0xfe /* max lun */, 0, 1, 1, abBuf, "get max lun") >= 0) + printf("max luns: %d\n", abBuf[0]); +#endif + + for (i = 0; i < 3; i++) + { + printf("i=%d\n", i); + + /* Send an INQUIRY command to ep 2 */ + memset(abBuf, 0, sizeof(abBuf)); + memcpy(abBuf, "USBC", 4); + *(uint32_t *)(&abBuf[4]) = 0x12330984 ; + //abBuf[8] = 0x08; + abBuf[8] = 0x24; + abBuf[0xc] = 0x80; + abBuf[0xe] = 0x06; /* cmd length */ + abBuf[0x0f] = 0x12; /* cmd - INQUIRY */ + abBuf[0x13] = 0x24; + + hex(abBuf, 31, "intquery req"); + if (send_bulk(OutEndPt, abBuf, 31) < 0) + return; + //usleep(15000); + + /* read result */ + memset(abBuf, 0, sizeof(abBuf)); + //printf("recv..\n"); + int cb = recv_bulk(InEndPt, abBuf, 36); + hex(abBuf, cb, "inquiry result"); + + /* sense? */ + memset(abBuf, 0, sizeof(abBuf)); + cb = recv_bulk(InEndPt, abBuf, 36); + hex(abBuf, cb, "inquiry sense?"); + usleep(150000); + } +} + +int reset(void) +{ + int i = 0; + printf("resetting...\n"); + return doioctl(USBDEVFS_RESET, &i, "reset"); +} + +int main(int argc, char **argv) +{ + g_fd = open(argv[1], O_RDWR); + if (g_fd < 0) + return bitch("open"); + + reset(); + + usb_set_connected(0, 1); + claim_if(0); + +// set_config(1); - the culprit! + set_interface(0, 0); + + msd(); + return 0; +} -- cgit v1.2.3