]> sigrok.org Git - libsigrok.git/commitdiff
kingst-la2016: move USB bulk transfer handling to helper routines
authorGerhard Sittig <redacted>
Sun, 20 Feb 2022 07:48:08 +0000 (08:48 +0100)
committerGerhard Sittig <redacted>
Tue, 22 Feb 2022 20:53:20 +0000 (21:53 +0100)
Concentrate all support code which handles USB bulk transfers for the
capture data download in a set of helper routines: memory allocation
and release, submission and cancellation including re-submission after
a previous completion.

Submit host side transfers earlier, between the configuration and the
start of USB bulk transfers in the device. Allocate transfers and their
buffers at the first acquisition start. Keep allocated buffers across
several acquisition periods. Relase transfers and their buffers when the
device closes. Allocate buffers of fixed size (always 256KiB, no longer
depends on the remainder of the currently downloaded capture data).

Acquisition stop cancels all currently submitted transfers, and does
postprocess their (cancelled) completion without re-submitting. This
avoids "spilling" pending transfers into the next acquisition cycle.

Name the helper routines in preparation of using multiple transfers.
Stick with a single transfer in this commit to simplify review. Use a
longer USB receive timeout (500ms for capture data, in contrast to
200ms for control transfers).

src/hardware/kingst-la2016/api.c
src/hardware/kingst-la2016/protocol.c
src/hardware/kingst-la2016/protocol.h

index 93bc4d668132c2452ebbdbf9ed4e2178926ab807..6eb802b2f43368166ff13d496d04cfecb56b15e0 100644 (file)
@@ -680,6 +680,8 @@ static int dev_close(struct sr_dev_inst *sdi)
        if (!usb->devhdl)
                return SR_ERR_BUG;
 
+       la2016_release_resources(sdi);
+
        if (WITH_DEINIT_IN_CLOSE)
                la2016_deinit_hardware(sdi);
 
index 6a133a5fa9c73631c82d6e664c38fc50391ed558..3ade5be5ecebc570cd73b0b42a0899afcec1fbe8 100644 (file)
@@ -943,6 +943,137 @@ SR_PRIV int la2016_upload_firmware(const struct sr_dev_inst *sdi,
        return SR_OK;
 }
 
+static void LIBUSB_CALL receive_transfer(struct libusb_transfer *xfer);
+
+static int la2016_usbxfer_release(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct libusb_transfer *xfer;
+
+       devc = sdi ? sdi->priv : NULL;
+       if (!devc)
+               return SR_ERR_ARG;
+
+       /* Release all USB transfers. */
+       xfer = devc->transfer;
+       devc->transfer = NULL;
+       if (!xfer)
+               return SR_OK;
+
+       g_free(xfer->buffer);
+       libusb_free_transfer(xfer);
+
+       return SR_OK;
+}
+
+static int la2016_usbxfer_allocate(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       size_t bufsize;
+       uint8_t *buffer;
+       struct libusb_transfer *xfer;
+
+       devc = sdi ? sdi->priv : NULL;
+       if (!devc)
+               return SR_ERR_ARG;
+
+       /* Transfers were already allocated before? */
+       if (devc->transfer)
+               return SR_OK;
+
+       /*
+        * Allocate all USB transfers and their buffers. Arrange for a
+        * buffer size which is within the device's capabilities, and
+        * is a multiple of the USB endpoint's size, to make use of the
+        * RAW_IO performance feature.
+        *
+        * Implementation detail: The LA2016_USB_BUFSZ value happens
+        * to match all those constraints. No additional arithmetics is
+        * required in this location.
+        */
+       bufsize = LA2016_USB_BUFSZ;
+       buffer = g_try_malloc(bufsize);
+       if (!buffer) {
+               sr_err("Cannot allocate USB transfer buffer.");
+               return SR_ERR_MALLOC;
+       }
+       xfer = libusb_alloc_transfer(0);
+       if (!xfer) {
+               sr_err("Cannot allocate USB transfer.");
+               g_free(buffer);
+               return SR_ERR_MALLOC;
+       }
+       xfer->buffer = buffer;
+       devc->transfer = xfer;
+       devc->transfer_bufsize = bufsize;
+
+       return SR_OK;
+}
+
+static int la2016_usbxfer_cancel_all(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct libusb_transfer *xfer;
+
+       devc = sdi ? sdi->priv : NULL;
+       if (!devc)
+               return SR_ERR_ARG;
+
+       /* Unconditionally cancel the transfer. Ignore errors. */
+       xfer = devc->transfer;
+       if (xfer)
+               libusb_cancel_transfer(xfer);
+
+       return SR_OK;
+}
+
+static int la2016_usbxfer_resubmit(const struct sr_dev_inst *sdi,
+       struct libusb_transfer *xfer)
+{
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       libusb_transfer_cb_fn cb;
+       int ret;
+
+       devc = sdi ? sdi->priv : NULL;
+       usb = sdi ? sdi->conn : NULL;
+       if (!devc || !usb)
+               return SR_ERR_ARG;
+
+       if (!xfer)
+               return SR_ERR_ARG;
+
+       cb = receive_transfer;
+       libusb_fill_bulk_transfer(xfer, usb->devhdl,
+               USB_EP_CAPTURE_DATA | LIBUSB_ENDPOINT_IN,
+               xfer->buffer, devc->transfer_bufsize,
+               cb, (void *)sdi, CAPTURE_TIMEOUT_MS);
+       ret = libusb_submit_transfer(xfer);
+       if (ret != 0) {
+               sr_err("Cannot submit USB transfer: %s.",
+                       libusb_error_name(ret));
+               return SR_ERR_IO;
+       }
+
+       return SR_OK;
+}
+
+static int la2016_usbxfer_submit_all(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       int ret;
+
+       devc = sdi ? sdi->priv : NULL;
+       if (!devc)
+               return SR_ERR_ARG;
+
+       ret = la2016_usbxfer_resubmit(sdi, devc->transfer);
+       if (ret != SR_OK)
+               return ret;
+
+       return SR_OK;
+}
+
 SR_PRIV int la2016_setup_acquisition(const struct sr_dev_inst *sdi,
        double voltage)
 {
@@ -975,6 +1106,10 @@ SR_PRIV int la2016_start_acquisition(const struct sr_dev_inst *sdi)
 {
        int ret;
 
+       ret = la2016_usbxfer_allocate(sdi);
+       if (ret != SR_OK)
+               return ret;
+
        ret = set_run_mode(sdi, RUNMODE_RUN);
        if (ret != SR_OK)
                return ret;
@@ -996,32 +1131,24 @@ static int la2016_stop_acquisition(const struct sr_dev_inst *sdi)
 SR_PRIV int la2016_abort_acquisition(const struct sr_dev_inst *sdi)
 {
        int ret;
-       struct dev_context *devc;
 
        ret = la2016_stop_acquisition(sdi);
        if (ret != SR_OK)
                return ret;
 
-       devc = sdi ? sdi->priv : NULL;
-       if (devc && devc->transfer)
-               libusb_cancel_transfer(devc->transfer);
+       (void)la2016_usbxfer_cancel_all(sdi);
 
        return SR_OK;
 }
 
-static int la2016_start_download(const struct sr_dev_inst *sdi,
-       libusb_transfer_cb_fn cb)
+static int la2016_start_download(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
-       struct sr_usb_dev_inst *usb;
        int ret;
        uint8_t wrbuf[REG_SAMPLING - REG_BULK]; /* Width of REG_BULK. */
        uint8_t *wrptr;
-       uint32_t to_read;
-       uint8_t *buffer;
 
        devc = sdi->priv;
-       usb = sdi->conn;
 
        ret = get_capture_info(sdi);
        if (ret != SR_OK)
@@ -1052,42 +1179,17 @@ static int la2016_start_download(const struct sr_dev_inst *sdi,
                sr_err("Cannot send USB bulk config.");
                return ret;
        }
-       ret = ctrl_out(sdi, CMD_BULK_START, 0x00, 0, NULL, 0);
+
+       ret = la2016_usbxfer_submit_all(sdi);
        if (ret != SR_OK) {
-               sr_err("Cannot unblock USB bulk transfers.");
+               sr_err("Cannot submit USB bulk transfers.");
                return ret;
        }
 
-       /*
-        * Pick a buffer size for all USB transfers. The buffer size
-        * must be a multiple of the endpoint packet size. And cannot
-        * exceed a maximum value.
-        */
-       to_read = devc->n_bytes_to_read;
-       if (to_read >= LA2016_USB_BUFSZ) /* Multiple transfers. */
-               to_read = LA2016_USB_BUFSZ;
-       to_read += LA2016_EP6_PKTSZ - 1;
-       to_read /= LA2016_EP6_PKTSZ;
-       to_read *= LA2016_EP6_PKTSZ;
-       buffer = g_try_malloc(to_read);
-       if (!buffer) {
-               sr_dbg("USB bulk transfer size %d bytes.", (int)to_read);
-               sr_err("Cannot allocate buffer for USB bulk transfer.");
-               return SR_ERR_MALLOC;
-       }
-
-       devc->transfer = libusb_alloc_transfer(0);
-       libusb_fill_bulk_transfer(devc->transfer,
-               usb->devhdl, USB_EP_CAPTURE_DATA | LIBUSB_ENDPOINT_IN,
-               buffer, to_read, cb, (void *)sdi, DEFAULT_TIMEOUT_MS);
-
-       ret = libusb_submit_transfer(devc->transfer);
-       if (ret != 0) {
-               sr_err("Cannot submit USB transfer: %s.", libusb_error_name(ret));
-               libusb_free_transfer(devc->transfer);
-               devc->transfer = NULL;
-               g_free(buffer);
-               return SR_ERR_IO;
+       ret = ctrl_out(sdi, CMD_BULK_START, 0x00, 0, NULL, 0);
+       if (ret != SR_OK) {
+               sr_err("Cannot start USB bulk transfers.");
+               return ret;
        }
 
        return SR_OK;
@@ -1192,13 +1294,13 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
 {
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
-       struct sr_usb_dev_inst *usb;
+       gboolean was_cancelled;
        int ret;
 
        sdi = transfer->user_data;
        devc = sdi->priv;
-       usb = sdi->conn;
 
+       was_cancelled = transfer->status == LIBUSB_TRANSFER_CANCELLED;
        sr_dbg("receive_transfer(): status %s received %d bytes.",
                libusb_error_name(transfer->status), transfer->actual_length);
        /*
@@ -1210,35 +1312,17 @@ static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
         */
        send_chunk(sdi, transfer->buffer, transfer->actual_length);
 
-       if (!devc->download_finished) {
-               uint32_t to_read;
-
-               /*
-                * Determine read size for the next USB transfer. Make
-                * the buffer size a multiple of the endpoint packet
-                * size. Don't exceed a maximum value.
-                */
-               to_read = devc->n_bytes_to_read;
-               if (to_read >= LA2016_USB_BUFSZ)
-                       to_read = LA2016_USB_BUFSZ;
-               to_read += LA2016_EP6_PKTSZ - 1;
-               to_read /= LA2016_EP6_PKTSZ;
-               to_read *= LA2016_EP6_PKTSZ;
-               libusb_fill_bulk_transfer(transfer,
-                       usb->devhdl, USB_EP_CAPTURE_DATA | LIBUSB_ENDPOINT_IN,
-                       transfer->buffer, to_read,
-                       receive_transfer, (void *)sdi, DEFAULT_TIMEOUT_MS);
-
-               ret = libusb_submit_transfer(transfer);
-               if (ret == 0)
+       /*
+        * Re-submit completed transfers (regardless of timeout or
+        * data reception), unless the transfer was cancelled when
+        * the acquisition was terminated or has completed.
+        */
+       if (!was_cancelled && !devc->download_finished) {
+               ret = la2016_usbxfer_resubmit(sdi, transfer);
+               if (ret == SR_OK)
                        return;
-               sr_err("Cannot submit another USB transfer: %s.",
-                       libusb_error_name(ret));
                devc->download_finished = TRUE;
        }
-
-       g_free(transfer->buffer);
-       libusb_free_transfer(transfer);
 }
 
 SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data)
@@ -1282,7 +1366,7 @@ SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data)
                /* Initiate the download of acquired sample data. */
                std_session_send_df_frame_begin(sdi);
                devc->frame_begin_sent = TRUE;
-               ret = la2016_start_download(sdi, receive_transfer);
+               ret = la2016_start_download(sdi);
                if (ret != SR_OK) {
                        sr_err("Cannot start acquisition data download.");
                        return FALSE;
@@ -1293,7 +1377,7 @@ SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data)
        }
 
        /* Handle USB reception. Drives sample data download. */
-       tv.tv_sec = tv.tv_usec = 0;
+       memset(&tv, 0, sizeof(tv));
        libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 
        /* Postprocess completion of sample data download. */
@@ -1302,7 +1386,10 @@ SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data)
 
                la2016_stop_acquisition(sdi);
                usb_source_remove(sdi->session, drvc->sr_ctx);
-               devc->transfer = NULL;
+
+               la2016_usbxfer_cancel_all(sdi);
+               memset(&tv, 0, sizeof(tv));
+               libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
 
                feed_queue_logic_flush(devc->feed_queue);
                feed_queue_logic_free(devc->feed_queue);
@@ -1510,6 +1597,11 @@ SR_PRIV int la2016_deinit_hardware(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
+SR_PRIV void la2016_release_resources(const struct sr_dev_inst *sdi)
+{
+       (void)la2016_usbxfer_release(sdi);
+}
+
 SR_PRIV int la2016_write_pwm_config(const struct sr_dev_inst *sdi, size_t idx)
 {
        return set_pwm_config(sdi, idx);
index 6204e85250cf658229f103e0b3d4d76629ac222e..c536617fcd47aaba97f4b44e75dc170c768cedec 100644 (file)
@@ -49,6 +49,7 @@
 
 /* USB communication timeout during regular operation. */
 #define DEFAULT_TIMEOUT_MS     200
+#define CAPTURE_TIMEOUT_MS     500
 
 /*
  * Check for MCU firmware to take effect after upload. Check the device
@@ -148,6 +149,7 @@ struct dev_context {
 
        struct feed_queue_logic *feed_queue;
        struct libusb_transfer *transfer;
+       size_t transfer_bufsize;
 };
 
 SR_PRIV int la2016_upload_firmware(const struct sr_dev_inst *sdi,
@@ -162,5 +164,6 @@ SR_PRIV int la2016_setup_acquisition(const struct sr_dev_inst *sdi,
 SR_PRIV int la2016_start_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int la2016_abort_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV void la2016_release_resources(const struct sr_dev_inst *sdi);
 
 #endif