From: Junjie Cao <[email protected]>

The isochronous NAK test exercises a TD sent to an interrupt endpoint.
Add a successful transfer to an isochronous endpoint as a positive
case for changes to endpoint type checks.

Configure a USB audio device and enable its streaming interface
through EP0 before submitting an OUT packet. Check that the TD
completes without errors and reports the full packet length.

Signed-off-by: Junjie Cao <[email protected]>
---
 tests/qtest/usb-hcd-uhci-test.c | 74 +++++++++++++++++++++++++++------
 1 file changed, 62 insertions(+), 12 deletions(-)

diff --git a/tests/qtest/usb-hcd-uhci-test.c b/tests/qtest/usb-hcd-uhci-test.c
index bad279e9a2..28d38bcc7c 100644
--- a/tests/qtest/usb-hcd-uhci-test.c
+++ b/tests/qtest/usb-hcd-uhci-test.c
@@ -21,6 +21,7 @@ static QOSState *qs;
 #define UHCI_FRAME_NS 1000000
 #define UHCI_PID_SETUP 0x2d
 #define UHCI_PID_IN    0x69
+#define UHCI_PID_OUT   0xe1
 #define UHCI_LINK_END  1
 #define UHCI_LINK_QH   2
 
@@ -109,17 +110,27 @@ static void uhci_expect_complete(UHCITransferTest *t, 
unsigned int len)
     uhci_writew(t, UHCI_USBCMD, 0);
 }
 
-static void uhci_transfer_init(UHCITransferTest *t)
+static void uhci_control_no_data(UHCITransferTest *t,
+                                 const uint8_t setup[8])
+{
+    qtest_memwrite(t->qs->qts, t->data, setup, 8);
+    uhci_submit(t, UHCI_PID_SETUP, 0, 8, false, false);
+    uhci_expect_complete(t, 8);
+    uhci_submit(t, UHCI_PID_IN, 0, 0, true, false);
+    uhci_expect_complete(t, 0);
+}
+
+static void uhci_transfer_init(UHCITransferTest *t, const char *usb_device)
 {
     const char *cmd = "-nodefaults -global pci-host-bridge.bypass-iommu=on "
-                      "-device piix3-usb-uhci,id=uhci,addr=1d.0 "
-                      "-device usb-kbd,bus=uhci.0,port=1";
+                      "-device piix3-usb-uhci,id=uhci,addr=1d.0";
     const uint8_t setup[] = { 0, 9, 1, 0, 0, 0, 0, 0 };
 
     if (!strcmp(qtest_get_arch(), "ppc64")) {
-        t->qs = qtest_spapr_boot("%s", cmd);
+        t->qs = qtest_spapr_boot("%s %s", cmd, usb_device);
     } else {
-        t->qs = qtest_pc_boot("-machine pc,pit=off %s", cmd);
+        t->qs = qtest_pc_boot("-machine pc,pit=off %s %s", cmd,
+                              usb_device);
     }
     qusb_pci_init_one(t->qs->pcibus, &t->hc, QPCI_DEVFN(0x1d, 0), 4);
     t->mem = guest_alloc(&t->qs->alloc, 0x3000);
@@ -136,12 +147,8 @@ static void uhci_transfer_init(UHCITransferTest *t)
                     UHCI_PORT_CCS | UHCI_PORT_EN);
     qpci_io_writel(t->hc.dev, t->hc.bar, UHCI_USBFLBASEADD, t->mem);
 
-    /* Configure the keyboard's interrupt endpoint through EP0 first. */
-    qtest_memwrite(t->qs->qts, t->data, setup, sizeof(setup));
-    uhci_submit(t, UHCI_PID_SETUP, 0, sizeof(setup), false, false);
-    uhci_expect_complete(t, sizeof(setup));
-    uhci_submit(t, UHCI_PID_IN, 0, 0, true, false);
-    uhci_expect_complete(t, 0);
+    /* Configure the device through EP0 before using a non-default endpoint. */
+    uhci_control_no_data(t, setup);
 }
 
 static void test_uhci_nak(gconstpointer opaque)
@@ -153,7 +160,7 @@ static void test_uhci_nak(gconstpointer opaque)
     uint32_t ctrl;
     unsigned int i;
 
-    uhci_transfer_init(&t);
+    uhci_transfer_init(&t, "-device usb-kbd,bus=uhci.0,port=1");
     qtest_memset(t.qs->qts, t.data, 0xa5, sizeof(report));
     uhci_submit(&t, UHCI_PID_IN, 1, sizeof(report), false, iso);
     for (i = 0; i < 3; i++) {
@@ -190,6 +197,48 @@ static void test_uhci_nak(gconstpointer opaque)
     qtest_shutdown(t.qs);
 }
 
+static void test_uhci_iso_audio_out(void)
+{
+    const uint8_t set_interface[] = { 1, 11, 1, 0, 1, 0, 0, 0 };
+    const unsigned int packet_size = 192;
+    UHCITransferTest t;
+    uint32_t ctrl;
+    unsigned int i;
+
+    if (!qtest_has_device("usb-audio")) {
+        g_test_skip("usb-audio is not available");
+        return;
+    }
+
+    uhci_transfer_init(&t, "-audiodev none,id=snd0 "
+                           "-device usb-audio,audiodev=snd0,"
+                           "bus=uhci.0,port=1");
+    /* Interface 1, alternate setting 1 enables the stereo OUT endpoint. */
+    uhci_control_no_data(&t, set_interface);
+
+    /* Full-speed audio altsetting 1 advertises OUT endpoint 1, 192 bytes. */
+    qtest_memset(t.qs->qts, t.data, 0x5a, packet_size);
+    uhci_submit(&t, UHCI_PID_OUT, 1, packet_size, false, true);
+    for (i = 0; i < 10; i++) {
+        ctrl = uhci_step(&t);
+        if (!(ctrl & TD_CTRL_ACTIVE)) {
+            break;
+        }
+    }
+    g_assert_cmpuint(i, <, 10);
+    g_assert_cmphex(ctrl & (TD_CTRL_ACTIVE | TD_CTRL_STALL | TD_CTRL_BABBLE |
+                            TD_CTRL_NAK | TD_CTRL_TIMEOUT), ==, 0);
+    g_assert_cmpuint((ctrl + 1) & 0x7ff, ==, packet_size);
+    g_assert_cmphex(qpci_io_readw(t.hc.dev, t.hc.bar, UHCI_USBSTS) &
+                    UHCI_STS_USBINT, ==, UHCI_STS_USBINT);
+    uhci_writew(&t, UHCI_USBCMD, 0);
+
+    qpci_iounmap(t.hc.dev, t.hc.bar);
+    uhci_deinit(&t.hc);
+    guest_free(&t.qs->alloc, t.mem);
+    qtest_shutdown(t.qs);
+}
+
 static void test_port(int port)
 {
     struct qhc uhci;
@@ -251,6 +300,7 @@ int main(int argc, char **argv)
         qtest_add_data_func("/uhci/pci/iso-nak", GUINT_TO_POINTER(true),
                             test_uhci_nak);
     }
+    qtest_add_func("/uhci/pci/iso-audio-out", test_uhci_iso_audio_out);
 
     if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
         qs = qtest_pc_boot("%s", cmd);
-- 
2.43.0


Reply via email to