[usb_testutils] Endpoint removal and finalization

Defer configuration and enabling of endpoint zero until the
callback handlers have been registered, except for enabling
SETUP reception which is unique to this endpoint.

Change-Id: Id9e3bdc34e28804e5eeeb9431d69ba50ce4c45f2
Signed-off-by: Adrian Lees <a.lees@lowrisc.org>
diff --git a/sw/device/lib/testing/usb_testutils.c b/sw/device/lib/testing/usb_testutils.c
index e0605b6..4831f9b 100644
--- a/sw/device/lib/testing/usb_testutils.c
+++ b/sw/device/lib/testing/usb_testutils.c
@@ -77,6 +77,7 @@
       if (ctx->out[ep].rx_callback) {
         ctx->out[ep].rx_callback(ctx->out[ep].ep_ctx, packet_info, buffer);
       } else {
+        // Note: this could happen following endpoint removal
         TRC_S("USB: unexpected RX ");
         TRC_I(endpoint, 8);
         CHECK_DIF_OK(
@@ -214,7 +215,7 @@
 }
 
 void usb_testutils_endpoint_setup(
-    usb_testutils_ctx_t *ctx, int ep,
+    usb_testutils_ctx_t *ctx, uint8_t ep,
     usb_testutils_out_transfer_mode_t out_mode, void *ep_ctx,
     void (*tx_done)(void *),
     void (*rx)(void *, dif_usbdev_rx_packet_info_t, dif_usbdev_buffer_t),
@@ -226,6 +227,46 @@
   usb_testutils_out_endpoint_setup(ctx, ep, out_mode, ep_ctx, rx, NULL);
 }
 
+void usb_testutils_in_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep) {
+  // Disable IN traffic
+  dif_usbdev_endpoint_id_t endpoint = {
+      .number = ep,
+      .direction = USBDEV_ENDPOINT_DIR_IN,
+  };
+  CHECK_DIF_OK(
+      dif_usbdev_endpoint_enable(ctx->dev, endpoint, kDifToggleDisabled));
+
+  // Remove callback handlers
+  ctx->in[ep].tx_done_callback = NULL;
+  ctx->in[ep].flush = NULL;
+  ctx->in[ep].reset = NULL;
+}
+
+void usb_testutils_out_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep) {
+  // Disable OUT traffic
+  dif_usbdev_endpoint_id_t endpoint = {
+      .number = ep,
+      .direction = USBDEV_ENDPOINT_DIR_OUT,
+  };
+  CHECK_DIF_OK(
+      dif_usbdev_endpoint_enable(ctx->dev, endpoint, kDifToggleDisabled));
+
+  // Return the rest of the OUT endpoint configuration to its default state
+  CHECK_DIF_OK(dif_usbdev_endpoint_set_nak_out_enable(ctx->dev, endpoint.number,
+                                                      kDifToggleDisabled));
+  CHECK_DIF_OK(dif_usbdev_endpoint_out_enable(ctx->dev, endpoint.number,
+                                              kDifToggleDisabled));
+
+  // Remove callback handlers
+  ctx->out[ep].rx_callback = NULL;
+  ctx->out[ep].reset = NULL;
+}
+
+void usb_testutils_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep) {
+  usb_testutils_in_endpoint_remove(ctx, ep);
+  usb_testutils_out_endpoint_remove(ctx, ep);
+}
+
 void usb_testutils_init(usb_testutils_ctx_t *ctx, bool pinflip,
                         bool en_diff_rcvr, bool tx_use_d_se0) {
   CHECK(ctx != NULL);
@@ -258,27 +299,24 @@
   // All about polling...
   CHECK_DIF_OK(dif_usbdev_irq_disable_all(ctx->dev, NULL));
 
-  // Provide buffers for any reception
+  // Provide buffers for any packet reception
   CHECK_DIF_OK(dif_usbdev_fill_available_fifo(ctx->dev, ctx->buffer_pool));
 
-  dif_usbdev_endpoint_id_t endpoint = {
-      .number = 0,
-      .direction = 1,
-  };
+  // Preemptively enable SETUP reception on endpoint zero for the
+  // Default Control Pipe; all other settings for that endpoint will be applied
+  // once the callback handlers are registered by a call to _endpoint_setup()
   CHECK_DIF_OK(
-      dif_usbdev_endpoint_enable(ctx->dev, endpoint, kDifToggleEnabled));
-  CHECK_DIF_OK(
-      dif_usbdev_endpoint_stall_enable(ctx->dev, endpoint, kDifToggleDisabled));
+      dif_usbdev_endpoint_setup_enable(ctx->dev, 0, kDifToggleEnabled));
+}
 
-  endpoint.direction = 0;
-  CHECK_DIF_OK(
-      dif_usbdev_endpoint_enable(ctx->dev, endpoint, kDifToggleEnabled));
-  CHECK_DIF_OK(
-      dif_usbdev_endpoint_stall_enable(ctx->dev, endpoint, kDifToggleDisabled));
-  CHECK_DIF_OK(dif_usbdev_endpoint_setup_enable(ctx->dev, endpoint.number,
-                                                kDifToggleEnabled));
-  CHECK_DIF_OK(dif_usbdev_endpoint_out_enable(ctx->dev, endpoint.number,
-                                              kDifToggleEnabled));
+void usb_testutils_fin(usb_testutils_ctx_t *ctx) {
+  // Remove the endpoints in reverse order so that Endpoint Zero goes down last
+  for (int ep = USBDEV_NUM_ENDPOINTS - 1; ep >= 0; ep--) {
+    usb_testutils_endpoint_remove(ctx, ep);
+  }
+
+  // Disconnect from the bus
+  CHECK_DIF_OK(dif_usbdev_interface_enable(ctx->dev, kDifToggleDisabled));
 }
 
 // `extern` declarations to give the inline functions in the
diff --git a/sw/device/lib/testing/usb_testutils.h b/sw/device/lib/testing/usb_testutils.h
index 910d511..ee3cb0c 100644
--- a/sw/device/lib/testing/usb_testutils.h
+++ b/sw/device/lib/testing/usb_testutils.h
@@ -69,13 +69,6 @@
   } out[USBDEV_NUM_ENDPOINTS];
 };
 
-/**
- * Call regularly to poll the usbdev interface
- *
- * @param ctx usbdev context pointer
- */
-void usb_testutils_poll(usb_testutils_ctx_t *ctx);
-
 typedef enum usb_testutils_out_transfer_mode {
   /**
    * The endpoint does not support OUT transactions.
@@ -128,7 +121,7 @@
     void (*reset)(void *));
 
 /**
- * Call to set up a pairt of IN and OUT endpoints.
+ * Call to set up a pair of IN and OUT endpoints.
  *
  * @param ctx usbdev context pointer
  * @param ep endpoint number
@@ -140,7 +133,7 @@
  * @param flush(void *ep_ctx) called every 16ms based USB host timebase
  * @param reset(void *ep_ctx) called when an USB link reset is detected
  */
-void usb_testutils_endpoint_setup(usb_testutils_ctx_t *ctx, int ep,
+void usb_testutils_endpoint_setup(usb_testutils_ctx_t *ctx, uint8_t ep,
                                   usb_testutils_out_transfer_mode_t out_mode,
                                   void *ep_ctx, void (*tx_done)(void *),
                                   void (*rx)(void *,
@@ -149,6 +142,30 @@
                                   void (*flush)(void *), void (*reset)(void *));
 
 /**
+ * Remove an IN endpoint.
+ *
+ * @param ctx usbdev context pointer
+ * @param ep endpoint number
+ */
+void usb_testutils_in_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep);
+
+/**
+ * Remove an OUT endpoint.
+ *
+ * @param ctx usbdev context pointer
+ * @param ep endpoint number
+ */
+void usb_testutils_out_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep);
+
+/**
+ * Remove a pair of IN and OUT endpoints
+ *
+ * @param ctx usbdev context pointer
+ * @param ep endpoint number
+ */
+void usb_testutils_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep);
+
+/**
  * Returns an indication of whether an endpoint is currently halted because
  * of the occurrence of an error.
  *
@@ -176,4 +193,18 @@
 void usb_testutils_init(usb_testutils_ctx_t *ctx, bool pinflip,
                         bool en_diff_rcvr, bool tx_use_d_se0);
 
+/**
+ * Call regularly to poll the usbdev interface
+ *
+ * @param ctx usbdev context pointer
+ */
+void usb_testutils_poll(usb_testutils_ctx_t *ctx);
+
+/**
+ * Finalize the usbdev interface
+ *
+ * @param ctx initialized usbdev context pointer
+ */
+void usb_testutils_fin(usb_testutils_ctx_t *ctx);
+
 #endif  // OPENTITAN_SW_DEVICE_LIB_TESTING_USB_TESTUTILS_H_