[usb_testutils] Support larger data stages Data stages of multiple packets are required for larger configuration descriptors and higher software layers. Includes double-buffering support for higher IN data rates. Modify tx_done callback to accept a result code. Change-Id: I1cbdbc68051b0630cc917f09c6a880adb91ea849 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 4831f9b..bd1a1fa 100644 --- a/sw/device/lib/testing/usb_testutils.c +++ b/sw/device/lib/testing/usb_testutils.c
@@ -14,6 +14,78 @@ static dif_usbdev_t usbdev; static dif_usbdev_buffer_pool_t buffer_pool; +// Internal function to create the packet that will form the next part of a +// larger buffer transfer +static bool usb_testutils_part_prepare(usb_testutils_ctx_t *ctx, + usb_testutils_transfer_t *transfer, + dif_usbdev_buffer_t *next_part, + bool *last) { + CHECK(ctx && transfer && last); + + // Allocate and fill a packet buffer + dif_result_t result = + dif_usbdev_buffer_request(ctx->dev, ctx->buffer_pool, next_part); + if (result != kDifOk) { + return false; + } + + // Determine the maximum bytes/packet + unsigned max_packet = USBDEV_MAX_PACKET_SIZE; + if (transfer->flags & kUsbTestutilsXfrMaxPacketSupplied) { + max_packet = (unsigned)(transfer->flags & kUsbTestutilsXfrMaxPacketMask); + } + + // How much are we sending this time? + unsigned part_len = transfer->length - transfer->offset; + if (part_len > max_packet) { + part_len = max_packet; + } + size_t bytes_written = 0U; + if (part_len) { + CHECK_DIF_OK(dif_usbdev_buffer_write(ctx->dev, next_part, + &transfer->buffer[transfer->offset], + part_len, &bytes_written)); + } + // Is this the last packet? + uint32_t next_offset = transfer->offset + bytes_written; + *last = true; + if (bytes_written == max_packet) { + if (next_offset < transfer->length || + (transfer->flags & kUsbTestutilsXfrEmployZLP)) { + *last = false; + } + } else { + CHECK(bytes_written < max_packet); + } + + transfer->offset = next_offset; + return true; +} + +// Internal function to perform the next part of a larger buffer transfer +static bool usb_testutils_transfer_next_part( + usb_testutils_ctx_t *ctx, uint8_t ep, usb_testutils_transfer_t *transfer) { + // Do we need to prepare a packet? + if (!transfer->next_valid && + !usb_testutils_part_prepare(ctx, transfer, &transfer->next_part, + &transfer->last)) { + return false; + } + + // Send the existing prepared packet + CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ep, &transfer->next_part)); + transfer->next_valid = false; + + // If we're double-buffering, request and fill another buffer immediately; + // we'll then be able to supply it much more promptly later... + if ((transfer->flags & kUsbTestutilsXfrDoubleBuffered) && !transfer->last) { + transfer->next_valid = usb_testutils_part_prepare( + ctx, transfer, &transfer->next_part, &transfer->last); + } + + return true; +} + void usb_testutils_poll(usb_testutils_ctx_t *ctx) { uint32_t istate; @@ -21,18 +93,9 @@ CHECK_DIF_OK(dif_usbdev_irq_get_state(ctx->dev, &istate)); if (!istate) { - // Nothing new to do right now, but keep buffers available for reception - CHECK_DIF_OK(dif_usbdev_fill_available_fifo(ctx->dev, ctx->buffer_pool)); return; } - // Record bus frame - if ((istate & (1u << kDifUsbdevIrqFrame))) { - // The first bus frame is 1 - CHECK_DIF_OK(dif_usbdev_status_get_frame(ctx->dev, &ctx->frame)); - ctx->got_frame = true; - } - // Process IN completions first so we get the fact that send completed // before processing a response to that transmission // This is also important for device IN performance @@ -40,28 +103,47 @@ uint16_t sentep; CHECK_DIF_OK(dif_usbdev_get_tx_sent(ctx->dev, &sentep)); TRC_C('a' + sentep); - for (unsigned ep = 0; ep < USBDEV_NUM_ENDPOINTS; ep++) { - if (sentep & (1 << ep)) { + unsigned ep = 0u; + while (sentep && ep < USBDEV_NUM_ENDPOINTS) { + if (sentep & (1u << ep)) { // Free up the buffer and optionally callback CHECK_DIF_OK( dif_usbdev_clear_tx_status(ctx->dev, ctx->buffer_pool, ep)); - if (ctx->in[ep].tx_done_callback) { - ctx->in[ep].tx_done_callback(ctx->in[ep].ep_ctx); + // If we have a larger transfer in progress, continue with that + usb_testutils_transfer_t *transfer = &ctx->in[ep].transfer; + usb_testutils_xfr_result_t res = kUsbTestutilsXfrResultOk; + bool done = true; + if (transfer->buffer) { + if (transfer->next_valid || !transfer->last) { + if (usb_testutils_transfer_next_part(ctx, ep, transfer)) { + done = false; + } else { + res = kUsbTestutilsXfrResultFailed; + } + } + if (done) { + // Larger buffer transfer now completed; forget the buffer + transfer->buffer = NULL; + } } + // Notify that we've sent the single packet, or larger buffer transfer + // is now complete + if (done && ctx->in[ep].tx_done_callback) { + ctx->in[ep].tx_done_callback(ctx->in[ep].ep_ctx, res); + } + sentep &= ~(1u << ep); } + ep++; } - - // Clear the interrupt sooner so that we can respond more promptly to - // subsequent transmitted packets - CHECK_DIF_OK(dif_usbdev_irq_acknowledge(ctx->dev, kDifUsbdevIrqPktSent)); - istate &= ~(1u << kDifUsbdevIrqPktSent); } // Keep buffers available for packet reception CHECK_DIF_OK(dif_usbdev_fill_available_fifo(ctx->dev, ctx->buffer_pool)); if (istate & (1u << kDifUsbdevIrqPktReceived)) { + // TODO: we run the risk of starving the IN side here if the rx_callback(s) + // are time-consuming while (true) { bool is_empty; CHECK_DIF_OK(dif_usbdev_status_get_rx_fifo_empty(ctx->dev, &is_empty)); @@ -104,6 +186,13 @@ // Clear the interrupts that we've received and handled CHECK_DIF_OK(dif_usbdev_irq_acknowledge_state(ctx->dev, istate)); + // Record bus frame + if ((istate & (1u << kDifUsbdevIrqFrame))) { + // The first bus frame is 1 + CHECK_DIF_OK(dif_usbdev_status_get_frame(ctx->dev, &ctx->frame)); + ctx->got_frame = true; + } + // Note: LinkInErr will be raised in response to a packet being NAKed by the // host which is not expected behavior on a physical USB but this is something // that the DPI model does to exercise packet resending when running @@ -158,10 +247,38 @@ // TODO Errors? What Errors? } -void usb_testutils_in_endpoint_setup(usb_testutils_ctx_t *ctx, uint8_t ep, - void *ep_ctx, void (*tx_done)(void *), - void (*flush)(void *), - void (*reset)(void *)) { +bool usb_testutils_transfer_send(usb_testutils_ctx_t *ctx, uint8_t ep, + const uint8_t *data, uint32_t length, + usb_testutils_xfr_flags_t flags) { + CHECK(ep < USBDEV_NUM_ENDPOINTS); + + usb_testutils_transfer_t *transfer = &ctx->in[ep].transfer; + if (transfer->buffer) { + // If there is an in-progress transfer, then we cannot accept another + return false; + } + + // Describe this transfer + transfer->buffer = data; + transfer->offset = 0U; + transfer->length = length; + transfer->flags = flags; + transfer->next_valid = false; + + if (!usb_testutils_transfer_next_part(ctx, ep, transfer)) { + // Forget about the attempted transfer + transfer->buffer = NULL; + return false; + } + + // Buffer transfer is underway... + return true; +} + +void usb_testutils_in_endpoint_setup( + usb_testutils_ctx_t *ctx, uint8_t ep, void *ep_ctx, + void (*tx_done)(void *, usb_testutils_xfr_result_t), void (*flush)(void *), + void (*reset)(void *)) { ctx->in[ep].ep_ctx = ep_ctx; ctx->in[ep].tx_done_callback = tx_done; ctx->in[ep].flush = flush; @@ -217,7 +334,7 @@ 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 (*tx_done)(void *, usb_testutils_xfr_result_t), void (*rx)(void *, dif_usbdev_rx_packet_info_t, dif_usbdev_buffer_t), void (*flush)(void *), void (*reset)(void *)) { usb_testutils_in_endpoint_setup(ctx, ep, ep_ctx, tx_done, flush, reset);
diff --git a/sw/device/lib/testing/usb_testutils.h b/sw/device/lib/testing/usb_testutils.h index ee3cb0c..6dcfc96 100644 --- a/sw/device/lib/testing/usb_testutils.h +++ b/sw/device/lib/testing/usb_testutils.h
@@ -12,6 +12,86 @@ #include "sw/device/lib/dif/dif_usbdev.h" #include "usb_testutils_diags.h" +// Result codes to rx/tx callback handlers +typedef enum { + /** + * Successful completion. + */ + kUsbTestutilsXfrResultOk = 0u, + /** + * Failed to transfer because of internal error, + * eg. buffer exhaustion. + */ + kUsbTestutilsXfrResultFailed = 1u, + /** + * Link reset interrupted transfer. + */ + kUsbTestutilsXfrResultLinkReset = 2u, + /** + * Canceled by suspend, endpoint removal or finalization. + */ + kUsbTestutilsXfrResultCanceled = 3u, +} usb_testutils_xfr_result_t; + +// Flags affecting the transfer of larger data buffers +typedef enum { + kUsbTestutilsXfrMaxPacketMask = 0x7fu, // Max packet size [0,0x40U] + /** + * Explicitly specify the maximum packet size; otherwise the device default + * of USBDEV_MAX_PACKET_SIZE shall be assumed. + */ + kUsbTestutilsXfrMaxPacketSupplied = 0x100u, + /** + * Employ double-buffering to minimize the response time to notification of + * each packet transmission. This does require that two device packet buffers + * be available for use, but it increases the transfer rate. + */ + kUsbTestutilsXfrDoubleBuffered = 0x200u, + /** + * Emit/Expect Zero Length Packet as termination of data stage in the event + * that the final packet of the transfer is maximum length + */ + kUsbTestutilsXfrEmployZLP = 0x400u, +} usb_testutils_xfr_flags_t; + +// In-progress larger buffer transfer to/from host +typedef struct usb_testutils_transfer { + /** + * Start of buffer for transfer + */ + const uint8_t *buffer; + /** + * Total number of bytes to be transferred to/from buffer + */ + uint32_t length; + /** + * Byte offset of the _next_ packet to be transferred + */ + uint32_t offset; + /** + * Flags modifying the transfer + */ + usb_testutils_xfr_flags_t flags; + /** + * Indicates that the last packet of the transfer has been reached; + * if 'next_valid' is true, then 'next_part' holds the last packet, already + * prepared for sending; if next_valid is false, then the last packet has + * already been supplied to usbdev and we're just awaiting the 'pkt_sent' + * interrupt. + */ + bool last; + /** + * The next part has been prepared and is ready to send to usbdev + */ + bool next_valid; + /** + * When sending IN data to the host, we may employ double-buffering and keep + * an additional buffer ready to be sent as soon as we're notified of the + * transfer of its predecessor + */ + dif_usbdev_buffer_t next_part; +} usb_testutils_transfer_t; + typedef struct usb_testutils_ctx usb_testutils_ctx_t; struct usb_testutils_ctx { @@ -38,7 +118,7 @@ /** * Callback for transmission of IN packet */ - void (*tx_done_callback)(void *); + void (*tx_done_callback)(void *, usb_testutils_xfr_result_t); /** * Callback for periodically flushing IN data to host */ @@ -47,6 +127,10 @@ * Callback for link reset */ void (*reset)(void *); + /** + * Current in-progress transfer, if any + */ + usb_testutils_transfer_t transfer; } in[USBDEV_NUM_ENDPOINTS]; /** @@ -91,22 +175,22 @@ /** * Call to set up IN endpoint. * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer * @param ep endpoint number * @param ep_ctx context pointer for callee * @param tx_done(void *ep_ctx) callback once send has been Acked * @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_in_endpoint_setup(usb_testutils_ctx_t *ctx, uint8_t ep, - void *ep_ctx, void (*tx_done)(void *), - void (*flush)(void *), - void (*reset)(void *)); +void usb_testutils_in_endpoint_setup( + usb_testutils_ctx_t *ctx, uint8_t ep, void *ep_ctx, + void (*tx_done)(void *, usb_testutils_xfr_result_t), void (*flush)(void *), + void (*reset)(void *)); /** * Call to set up OUT endpoint. * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer * @param ep endpoint number * @param out_mode the transfer mode for OUT transactions * @param ep_ctx context pointer for callee @@ -123,7 +207,7 @@ /** * Call to set up a pair of IN and OUT endpoints. * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer * @param ep endpoint number * @param out_mode the transfer mode for OUT transactions * @param ep_ctx context pointer for callee @@ -133,18 +217,17 @@ * @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, 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), - void (*flush)(void *), void (*reset)(void *)); +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 *, usb_testutils_xfr_result_t), + void (*rx)(void *, dif_usbdev_rx_packet_info_t, dif_usbdev_buffer_t), + void (*flush)(void *), void (*reset)(void *)); /** * Remove an IN endpoint. * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer * @param ep endpoint number */ void usb_testutils_in_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep); @@ -152,7 +235,7 @@ /** * Remove an OUT endpoint. * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer * @param ep endpoint number */ void usb_testutils_out_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep); @@ -160,7 +243,7 @@ /** * Remove a pair of IN and OUT endpoints * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer * @param ep endpoint number */ void usb_testutils_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep); @@ -169,7 +252,7 @@ * Returns an indication of whether an endpoint is currently halted because * of the occurrence of an error. * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer * @param ep endpoint number * @return true iff the endpoint is halted as a result of an error condition */ @@ -182,7 +265,7 @@ * Does not connect the device, since the default endpoint is not yet enabled. * See usb_testutils_connect(). * - * @param ctx uninitialized usbdev context pointer + * @param ctx uninitialized usb test utils context pointer * @param pinflip boolean to indicate if PHY should be configured for D+/D- flip * @param en_diff_rcvr boolean to indicate if PHY should enable an external * differential receiver, activating the single-ended D @@ -194,16 +277,40 @@ bool en_diff_rcvr, bool tx_use_d_se0); /** + * Send a larger data transfer from the given endpoint + * + * The usb_testutils layer will, if necessary, break this transfer into multiple + * packet buffers to be transferred in turn across the USB. The caller shall be + * notified via the tx_done_callback handler of successful completion of the + * entire transfer, or failure, and the caller must guarantee the availability + * of the supplied data throughout the operation. + * + * @param ctx usb test utils context pointer + * @param ep endpoint number + * @param data buffer of data to be transferred + * @param length number of bytes to be transferred + * @param flags flags modifying the transfer operation + * @return true iff the data has been accepted for transmission + */ +bool usb_testutils_transfer_send(usb_testutils_ctx_t *ctx, uint8_t ep, + const uint8_t *data, uint32_t length, + usb_testutils_xfr_flags_t flags); + +/** * Call regularly to poll the usbdev interface * - * @param ctx usbdev context pointer + * @param ctx usb test utils context pointer */ void usb_testutils_poll(usb_testutils_ctx_t *ctx); /** * Finalize the usbdev interface * - * @param ctx initialized usbdev context pointer + * Removes all endpoint handlers and disconnects the device from the USB. + * This should be used only if the USB device is no longer required, or if it is + * required to be restarted with, for example, a different bus configuration. + * + * @param ctx initialized usb test utils context pointer */ void usb_testutils_fin(usb_testutils_ctx_t *ctx);
diff --git a/sw/device/lib/testing/usb_testutils_controlep.c b/sw/device/lib/testing/usb_testutils_controlep.c index 6866aab..aada26d 100644 --- a/sw/device/lib/testing/usb_testutils_controlep.c +++ b/sw/device/lib/testing/usb_testutils_controlep.c
@@ -123,14 +123,31 @@ CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer)); return kUsbTestutilsCtWaitIn; } else if ((wValue & 0xff00) == 0x200) { + usb_testutils_xfr_flags_t flags = kUsbTestutilsXfrDoubleBuffered; + // Configuration descriptor len = ctctx->cfg_dscr_len; if (wLength < len) { len = wLength; + } else if (wLength > len) { + // Since we're not sending as much as requested, we may need to use + // a Zero Length Packet to mark the end of the data stage + flags |= kUsbTestutilsXfrEmployZLP; } - CHECK_DIF_OK(dif_usbdev_buffer_write(ctx->dev, &buffer, ctctx->cfg_dscr, - len, &bytes_written)); - CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer)); + + if (len >= USBDEV_MAX_PACKET_SIZE) { + CHECK_DIF_OK( + dif_usbdev_buffer_return(ctx->dev, ctx->buffer_pool, &buffer)); + + if (!usb_testutils_transfer_send(ctx, 0U, ctctx->cfg_dscr, len, + flags)) { + return kUsbTestutilsCtError; + } + } else { + CHECK_DIF_OK(dif_usbdev_buffer_write( + ctx->dev, &buffer, ctctx->cfg_dscr, len, &bytes_written)); + CHECK_DIF_OK(dif_usbdev_send(ctx->dev, ctctx->ep, &buffer)); + } return kUsbTestutilsCtWaitIn; } return kUsbTestutilsCtError; // unknown @@ -267,7 +284,7 @@ return kUsbTestutilsCtError; } -static void ctrl_tx_done(void *ctctx_v) { +static void ctrl_tx_done(void *ctctx_v, usb_testutils_xfr_result_t result) { usb_testutils_controlep_ctx_t *ctctx = (usb_testutils_controlep_ctx_t *)ctctx_v; usb_testutils_ctx_t *ctx = ctctx->ctx; @@ -353,7 +370,7 @@ .number = 0, .direction = USBDEV_ENDPOINT_DIR_IN, }; - // Enable responding with STALL. Will be cleared by the HW. + // Enable responding with STALL. Will be cleared by the HW upon next SETUP. CHECK_DIF_OK( dif_usbdev_endpoint_stall_enable(ctx->dev, endpoint, kDifToggleEnabled)); endpoint.direction = USBDEV_ENDPOINT_DIR_OUT;
diff --git a/sw/device/tests/usbdev_stream_test.c b/sw/device/tests/usbdev_stream_test.c index a119324..dc4cba5 100644 --- a/sw/device/tests/usbdev_stream_test.c +++ b/sw/device/tests/usbdev_stream_test.c
@@ -36,13 +36,6 @@ #define STREAMS_MAX 11U #endif -// TODO - currently we are unable to send the configuration descriptor -// if we try to describe more than two bidirectional endpoints -#if STREAMS_MAX > 2U -#undef STREAMS_MAX -#define STREAMS_MAX 2U -#endif - // Number of streams to be tested #ifndef NUM_STREAMS #define NUM_STREAMS STREAMS_MAX @@ -216,15 +209,14 @@ /** * Configuration values for USB. - * TODO - dynamically construct a config descriptor appropriate to the test; - * this would avoid creating unusable ports on the host and also provide - * a little more testing */ static const uint8_t config_descriptors[] = { USB_CFG_DSCR_HEAD(USB_CFG_DSCR_LEN + STREAMS_MAX * (USB_INTERFACE_DSCR_LEN + 2 * USB_EP_DSCR_LEN), STREAMS_MAX), + // Up to 11 interfaces and STREAMS_MAX in the descriptor head specifies how + // many of the interfaces will be declared to the host VEND_INTERFACE_DSCR(0, 2, 0x50, 1), USB_BULK_EP_DSCR(0, 1U, USBDEV_MAX_PACKET_SIZE, 0), USB_BULK_EP_DSCR(1, 1U, USBDEV_MAX_PACKET_SIZE, 0), @@ -232,6 +224,42 @@ VEND_INTERFACE_DSCR(1, 2, 0x50, 1), USB_BULK_EP_DSCR(0, 2U, USBDEV_MAX_PACKET_SIZE, 0), USB_BULK_EP_DSCR(1, 2U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(2, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 3U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 3U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(3, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 4U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 4U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(4, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 5U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 5U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(5, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 6U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 6U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(6, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 7U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 7U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(7, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 8U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 8U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(8, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 9U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 9U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(9, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 10U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 10U, USBDEV_MAX_PACKET_SIZE, 0), + + VEND_INTERFACE_DSCR(10, 2, 0x50, 1), + USB_BULK_EP_DSCR(0, 11U, USBDEV_MAX_PACKET_SIZE, 0), + USB_BULK_EP_DSCR(1, 11U, USBDEV_MAX_PACKET_SIZE, 0), }; /** @@ -414,7 +442,7 @@ } // Callback for successful buffer transmission -static void strm_tx_done(void *stream_v) { +static void strm_tx_done(void *stream_v, usb_testutils_xfr_result_t result) { usbdev_stream_t *s = (usbdev_stream_t *)stream_v; usbdev_stream_test_ctx_t *ctx = s->ctx; usb_testutils_ctx_t *usbdev = ctx->usbdev;