From: Junjie Cao <[email protected]> Start a control request, then submit an isochronous IN transaction to EP0. The iTD must complete without reading data, and the pending control request must still return the full descriptor through qTDs.
Removing the endpoint type check in ehci_process_itd() makes the iTD consume the descriptor and fails the test. Signed-off-by: Junjie Cao <[email protected]> --- tests/qtest/usb-hcd-ehci-test.c | 75 ++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/tests/qtest/usb-hcd-ehci-test.c b/tests/qtest/usb-hcd-ehci-test.c index 1a8019dab4..66070ee5c1 100644 --- a/tests/qtest/usb-hcd-ehci-test.c +++ b/tests/qtest/usb-hcd-ehci-test.c @@ -30,8 +30,16 @@ static struct qhc ehci1; #define EHCI_QTD_PID_OUT 0 #define EHCI_QTD_PID_IN 1 #define EHCI_QTD_PID_SETUP 2 +#define EHCI_ITD_ACTIVE (1U << 31) +#define EHCI_ITD_IOC (1 << 15) +#define EHCI_ITD_IN (1 << 11) #define EHCI_FRAME_NS 1000000 +enum EHCIControlScenario { + EHCI_CONTROL_ONLY, + EHCI_ISO_IN_TO_CONTROL, +}; + typedef struct EHCITransferTest { QOSState *qs; struct qhc hc; @@ -39,7 +47,9 @@ typedef struct EHCITransferTest { uint64_t mem; uint64_t qh; uint64_t qtd; + uint64_t itd; uint64_t data; + uint64_t isodata; } EHCITransferTest; static uint32_t ehci_readl(EHCITransferTest *t, unsigned int reg) @@ -74,11 +84,13 @@ static void ehci_transfer_init(EHCITransferTest *t) "-device usb-kbd,bus=ehci.0,port=1,usb_version=2"); qusb_pci_init_one(t->qs->pcibus, &t->hc, QPCI_DEVFN(0x1d, 7), 0); t->opbase = qpci_io_readb(t->hc.dev, t->hc.bar, CAPLENGTH); - t->mem = guest_alloc(&t->qs->alloc, 0x2000); - t->qh = t->mem; - t->qtd = t->mem + 0x100; - t->data = t->mem + 0x1000; - qtest_memset(t->qs->qts, t->mem, 0, 0x2000); + t->mem = guest_alloc(&t->qs->alloc, 0x4000); + t->qh = t->mem + 0x1000; + t->qtd = t->mem + 0x1100; + t->itd = t->mem + 0x1200; + t->data = t->mem + 0x2000; + t->isodata = t->mem + 0x3000; + qtest_memset(t->qs->qts, t->mem, 0, 0x4000); ehci_writel(t, USBCMD, USBCMD_HCRESET); ehci_writel(t, CONFIGFLAG, 1); @@ -135,15 +147,59 @@ static void ehci_control_packet(EHCITransferTest *t, unsigned int pid, ehci_stop_schedules(t); } -static void test_ehci_control(void) +static void ehci_iso_to_control(EHCITransferTest *t) +{ + QTestState *qts = t->qs->qts; + uint8_t buffer[18]; + uint32_t transaction; + unsigned int i; + + qtest_memset(qts, t->isodata, 0xa5, sizeof(buffer)); + qtest_writel(qts, t->itd, EHCI_LINK_TERMINATE); + qtest_writel(qts, t->itd + 4, EHCI_ITD_ACTIVE | EHCI_ITD_IOC | + (sizeof(buffer) << 16)); + qtest_writel(qts, t->itd + 36, t->isodata); /* Address 0, endpoint 0. */ + qtest_writel(qts, t->itd + 40, 64 | EHCI_ITD_IN); + qtest_writel(qts, t->itd + 44, 1); /* One transaction per microframe. */ + for (i = 0; i < 1024; i++) { + qtest_writel(qts, t->mem + 4 * i, t->itd); + } + ehci_writel(t, USBSTS, USBSTS_RO_MASK); + ehci_writel(t, PERIODICLISTBASE, t->mem); + ehci_writel(t, USBCMD, USBCMD_RUNSTOP | USBCMD_PSE); + for (i = 0; i < 10; i++) { + qtest_clock_step(qts, EHCI_FRAME_NS); + transaction = qtest_readl(qts, t->itd + 4); + if (!(transaction & EHCI_ITD_ACTIVE)) { + break; + } + } + g_assert_cmpuint(i, <, 10); + /* A non-iso endpoint must not transfer data or receive the packet. */ + g_assert_cmphex(transaction, ==, EHCI_ITD_IOC); + qtest_memread(qts, t->isodata, buffer, sizeof(buffer)); + for (i = 0; i < sizeof(buffer); i++) { + g_assert_cmphex(buffer[i], ==, 0xa5); + } + g_assert_cmphex(ehci_readl(t, USBSTS) & + (USBSTS_INT | USBSTS_ERRINT | USBSTS_HSE), ==, USBSTS_INT); + ehci_stop_schedules(t); +} + +static void test_ehci_control(gconstpointer opaque) { const uint8_t setup[] = { 0x80, 6, 0, 1, 0, 0, 18, 0 }; + unsigned int scenario = GPOINTER_TO_UINT(opaque); EHCITransferTest t; uint8_t descriptor[18]; ehci_transfer_init(&t); qtest_memwrite(t.qs->qts, t.data, setup, sizeof(setup)); ehci_control_packet(&t, EHCI_QTD_PID_SETUP, sizeof(setup)); + if (scenario != EHCI_CONTROL_ONLY) { + ehci_iso_to_control(&t); + } + /* The invalid iTD must leave the pending control transfer intact. */ qtest_memset(t.qs->qts, t.data, 0xa5, sizeof(descriptor)); ehci_control_packet(&t, EHCI_QTD_PID_IN, sizeof(descriptor)); qtest_memread(t.qs->qts, t.data, descriptor, sizeof(descriptor)); @@ -300,7 +356,12 @@ int main(int argc, char **argv) qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2); qtest_add_func("/ehci/pci/ehci-port-3-hotplug", pci_ehci_port_hotplug); if (qtest_has_device("usb-kbd")) { - qtest_add_func("/ehci/pci/control", test_ehci_control); + qtest_add_data_func("/ehci/pci/control", + GUINT_TO_POINTER(EHCI_CONTROL_ONLY), + test_ehci_control); + qtest_add_data_func("/ehci/pci/iso-in-to-control", + GUINT_TO_POINTER(EHCI_ISO_IN_TO_CONTROL), + test_ehci_control); } qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7," -- 2.43.0
