From: Junjie Cao <[email protected]>

The MFINDEX test leaves the audio streaming interface disabled and
checks scheduling with a transfer that stalls. Add a successful
isochronous transfer so changes to endpoint checks are also tested
with a configured device.

Enable the audio streaming interface through EP0, then check the OUT
transfer event for success, zero residual length and the expected
slot, endpoint and TRB pointer.

Signed-off-by: Junjie Cao <[email protected]>
---
 tests/qtest/usb-hcd-xhci-test.c | 95 ++++++++++++++++++++++++++++++---
 1 file changed, 89 insertions(+), 6 deletions(-)

diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c
index 2e173af750..c14c19988e 100644
--- a/tests/qtest/usb-hcd-xhci-test.c
+++ b/tests/qtest/usb-hcd-xhci-test.c
@@ -42,13 +42,18 @@
 
 #define TRB_C                   (1 << 0)
 #define TRB_TR_IOC              (1 << 5)
+#define TRB_TR_IDT              (1 << 6)
+#define TRB_TR_DIR              (1 << 16)
 #define TRB_TR_SIA              (1U << 31)
 #define TRB_TYPE(t)             ((t) << 10)
 #define TRB_GET_TYPE(control)   (((control) >> 10) & 0x3f)
 #define TRB_GET_CCODE(status)   ((status) >> 24)
 #define TRB_GET_SLOT(control)   ((control) >> 24)
+#define TRB_GET_EPID(control)   (((control) >> 16) & 0x1f)
 
 #define TR_ISOCH                5
+#define TR_SETUP                2
+#define TR_STATUS               4
 #define CR_ENABLE_SLOT          9
 #define CR_ADDRESS_DEVICE       11
 #define CR_CONFIGURE_ENDPOINT   12
@@ -69,8 +74,8 @@ typedef struct XHCITest {
     QPCIBus *bus;
     struct qhc hc;
     uint32_t oper, runtime, doorbell;
-    uint64_t cmd_ring, event_ring, input_ctx;
-    unsigned int cmd_idx, event_idx;
+    uint64_t cmd_ring, event_ring, input_ctx, ep0_ring;
+    unsigned int cmd_idx, event_idx, ep0_idx;
     unsigned int port, slot;
 } XHCITest;
 
@@ -204,7 +209,8 @@ static void xhci_write_trb(XHCITest *x, uint64_t addr, 
uint64_t parameter,
 }
 
 /* Fetch the next event if there is one. Does not advance the clock. */
-static bool xhci_next_event(XHCITest *x, uint32_t *status, uint32_t *control)
+static bool xhci_next_event_full(XHCITest *x, uint64_t *parameter,
+                                 uint32_t *status, uint32_t *control)
 {
     uint64_t addr = x->event_ring + 16 * x->event_idx;
     uint32_t c = qtest_readl(x->qts, addr + 12);
@@ -212,6 +218,9 @@ static bool xhci_next_event(XHCITest *x, uint32_t *status, 
uint32_t *control)
     if (!(c & TRB_C)) {
         return false;
     }
+    if (parameter) {
+        *parameter = qtest_readq(x->qts, addr);
+    }
     if (status) {
         *status = qtest_readl(x->qts, addr + 8);
     }
@@ -224,6 +233,11 @@ static bool xhci_next_event(XHCITest *x, uint32_t *status, 
uint32_t *control)
     return true;
 }
 
+static bool xhci_next_event(XHCITest *x, uint32_t *status, uint32_t *control)
+{
+    return xhci_next_event_full(x, NULL, status, control);
+}
+
 static unsigned int xhci_command(XHCITest *x, uint64_t parameter,
                                  uint32_t control)
 {
@@ -246,7 +260,7 @@ static unsigned int xhci_command(XHCITest *x, uint64_t 
parameter,
  */
 static void xhci_test_start(XHCITest *x, const char *usb_device)
 {
-    uint64_t dcbaa, erst, ep0_ring;
+    uint64_t dcbaa, erst;
     unsigned int maxports;
 
     memset(x, 0, sizeof(*x));
@@ -302,14 +316,39 @@ static void xhci_test_start(XHCITest *x, const char 
*usb_device)
     qtest_writel(x->qts, x->input_ctx + 0x20, 1 << 27);
     qtest_writel(x->qts, x->input_ctx + 0x24, (x->port + 1) << 16);
     /* ep0 context */
-    ep0_ring = xhci_alloc_page(x);
+    x->ep0_ring = xhci_alloc_page(x);
     qtest_writel(x->qts, x->input_ctx + 0x44,
                  (64 << 16) | (EP_TYPE_CONTROL << 3));
-    qtest_writeq(x->qts, x->input_ctx + 0x48, ep0_ring | 1);
+    qtest_writeq(x->qts, x->input_ctx + 0x48, x->ep0_ring | 1);
     xhci_command(x, x->input_ctx,
                  TRB_TYPE(CR_ADDRESS_DEVICE) | (x->slot << 24));
 }
 
+static void xhci_control_no_data(XHCITest *x, const uint8_t setup[8])
+{
+    uint64_t setup_packet = 0, parameter;
+    uint32_t status, control;
+    unsigned int i;
+
+    for (i = 0; i < 8; i++) {
+        setup_packet |= (uint64_t)setup[i] << (i * 8);
+    }
+    xhci_write_trb(x, x->ep0_ring + 16 * x->ep0_idx++, setup_packet, 8,
+                   TRB_TYPE(TR_SETUP) | TRB_TR_IDT | TRB_C);
+    xhci_write_trb(x, x->ep0_ring + 16 * x->ep0_idx++, 0, 0,
+                   TRB_TYPE(TR_STATUS) | TRB_TR_DIR | TRB_TR_IOC | TRB_C);
+    xhci_writel(x, x->doorbell + 4 * x->slot, 1);
+
+    g_assert_true(xhci_next_event_full(x, &parameter, &status, &control));
+    g_assert_cmpuint(TRB_GET_TYPE(control), ==, ER_TRANSFER);
+    g_assert_cmpuint(TRB_GET_CCODE(status), ==, CC_SUCCESS);
+    g_assert_cmpuint(status & 0xffffff, ==, 0);
+    g_assert_cmpuint(TRB_GET_SLOT(control), ==, x->slot);
+    g_assert_cmpuint(TRB_GET_EPID(control), ==, 1);
+    g_assert_cmphex(parameter, ==,
+                    x->ep0_ring + 16 * (x->ep0_idx - 1));
+}
+
 /* Returns the address of the transfer ring. */
 static uint64_t xhci_configure_ep(XHCITest *x, unsigned int epid,
                                   unsigned int type, unsigned int interval,
@@ -423,6 +462,48 @@ static void test_xhci_isoch_ep_type_mismatch(void)
     xhci_test_end(&x);
 }
 
+static void test_xhci_isoch_audio_out(void)
+{
+    const uint8_t set_configuration[] = { 0, 9, 1, 0, 0, 0, 0, 0 };
+    const uint8_t set_interface[] = { 1, 11, 1, 0, 1, 0, 0, 0 };
+    const unsigned int packet_size = 192;
+    const unsigned int interval = 3; /* bInterval 1: one full-speed frame. */
+    XHCITest x;
+    uint32_t status, control;
+    uint64_t parameter, ring, data;
+
+    if (!xhci_test_supported("usb-audio")) {
+        return;
+    }
+
+    xhci_test_start(&x, "-audiodev none,id=snd0 "
+                        "-device usb-audio,audiodev=snd0");
+    xhci_control_no_data(&x, set_configuration);
+    xhci_control_no_data(&x, set_interface);
+
+    ring = xhci_configure_ep(&x, 2, EP_TYPE_ISOCH_OUT, interval,
+                             packet_size);
+    data = xhci_alloc_page(&x);
+    qtest_memset(x.qts, data, 0x5a, packet_size);
+    xhci_write_trb(&x, ring, data, packet_size,
+                   TRB_TYPE(TR_ISOCH) | TRB_TR_SIA | TRB_TR_IOC | TRB_C);
+    xhci_writel(&x, x.doorbell + 4 * x.slot, 2);
+    g_assert_false(xhci_next_event(&x, NULL, NULL));
+
+    qtest_clock_step(x.qts, (1ULL << interval) * XHCI_MICROFRAME_NS);
+    g_assert_true(xhci_next_event_full(&x, &parameter, &status, &control));
+    g_assert_cmpuint(TRB_GET_TYPE(control), ==, ER_TRANSFER);
+    g_assert_cmpuint(TRB_GET_CCODE(status), ==, CC_SUCCESS);
+    g_assert_cmpuint(status & 0xffffff, ==, 0);
+    g_assert_cmpuint(TRB_GET_SLOT(control), ==, x.slot);
+    g_assert_cmpuint(TRB_GET_EPID(control), ==, 2);
+    g_assert_cmphex(parameter, ==, ring);
+    g_assert_cmphex(xhci_readl(&x, x.oper + XHCI_USBSTS) &
+                    (USBSTS_HCH | USBSTS_HCE), ==, 0);
+
+    xhci_test_end(&x);
+}
+
 int main(int argc, char **argv)
 {
     int ret;
@@ -441,6 +522,8 @@ int main(int argc, char **argv)
                    test_xhci_isoch_mfindex_32bit);
     qtest_add_func("/xhci/pci/isoch/ep-type-mismatch",
                    test_xhci_isoch_ep_type_mismatch);
+    qtest_add_func("/xhci/pci/isoch/audio-out",
+                   test_xhci_isoch_audio_out);
 
     qtest_start("-device nec-usb-xhci,id=xhci"
                 " -drive id=drive0,if=none,file=null-co://,"
-- 
2.43.0


Reply via email to