]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/fx2lafw/protocol.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / fx2lafw / protocol.c
index 9ca749a50bd6f3fea41b254e87121f55bb9a1ffd..8854f5a69afd7221931b606b6d0c24a6001426c9 100644 (file)
@@ -76,7 +76,7 @@ static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid)
        return SR_OK;
 }
 
-SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
+static int command_start_acquisition(const struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
        struct sr_usb_dev_inst *usb;
@@ -112,7 +112,7 @@ SR_PRIV int fx2lafw_command_start_acquisition(const struct sr_dev_inst *sdi)
        sr_dbg("GPIF delay = %d, clocksource = %sMHz.", delay,
                (cmd.flags & CMD_START_FLAGS_CLK_48MHZ) ? "48" : "30");
 
-       if (delay <= 0 || delay > MAX_SAMPLE_DELAY) {
+       if (delay < 0 || delay > MAX_SAMPLE_DELAY) {
                sr_err("Unable to sample at %" PRIu64 "Hz.", samplerate);
                return SR_ERR;
        }
@@ -147,7 +147,7 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
        struct dev_context *devc;
        struct drv_context *drvc;
        struct version_info vi;
-       int ret, i, device_count;
+       int ret = SR_ERR, i, device_count;
        uint8_t revid;
        char connection_id[64];
 
@@ -155,10 +155,6 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
        devc = sdi->priv;
        usb = sdi->conn;
 
-       if (sdi->status == SR_ST_ACTIVE)
-               /* Device is already in use. */
-               return SR_ERR;
-
        device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
        if (device_count < 0) {
                sr_err("Failed to get device list: %s.",
@@ -178,7 +174,9 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                        /*
                         * Check device by its physical USB bus/port address.
                         */
-                       usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+                       if (usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)) < 0)
+                               continue;
+
                        if (strcmp(sdi->connection_id, connection_id))
                                /* This is not the one. */
                                continue;
@@ -194,6 +192,7 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                } else {
                        sr_err("Failed to open device: %s.",
                               libusb_error_name(ret));
+                       ret = SR_ERR;
                        break;
                }
 
@@ -202,7 +201,8 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                                if ((ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE)) < 0) {
                                        sr_err("Failed to detach kernel driver: %s.",
                                                libusb_error_name(ret));
-                                       return SR_ERR;
+                                       ret = SR_ERR;
+                                       break;
                                }
                        }
                }
@@ -231,7 +231,6 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                        break;
                }
 
-               sdi->status = SR_ST_ACTIVE;
                sr_info("Opened device on %d.%d (logical) / %s (physical), "
                        "interface %d, firmware %d.%d.",
                        usb->bus, usb->address, connection_id,
@@ -240,14 +239,14 @@ SR_PRIV int fx2lafw_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di)
                sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.",
                        revid, (revid != 1) ? " (FX2)" : "A (FX2LP)");
 
+               ret = SR_OK;
+
                break;
        }
-       libusb_free_device_list(devlist, 1);
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR;
+       libusb_free_device_list(devlist, 1);
 
-       return SR_OK;
+       return ret;
 }
 
 SR_PRIV struct dev_context *fx2lafw_dev_new(void)
@@ -256,11 +255,14 @@ SR_PRIV struct dev_context *fx2lafw_dev_new(void)
 
        devc = g_malloc0(sizeof(struct dev_context));
        devc->profile = NULL;
+       devc->channel_names = NULL;
        devc->fw_updated = 0;
        devc->cur_samplerate = 0;
+       devc->limit_frames = 1;
        devc->limit_samples = 0;
        devc->capture_ratio = 0;
        devc->sample_wide = FALSE;
+       devc->num_frames = 0;
        devc->stl = NULL;
 
        return devc;
@@ -340,7 +342,7 @@ static void resubmit_transfer(struct libusb_transfer *transfer)
 
 }
 
-SR_PRIV void mso_send_data_proc(struct sr_dev_inst *sdi,
+static void mso_send_data_proc(struct sr_dev_inst *sdi,
        uint8_t *data, size_t length, size_t sample_width)
 {
        size_t i;
@@ -392,7 +394,7 @@ SR_PRIV void mso_send_data_proc(struct sr_dev_inst *sdi,
        sr_session_send(sdi, &analog_packet);
 }
 
-SR_PRIV void la_send_data_proc(struct sr_dev_inst *sdi,
+static void la_send_data_proc(struct sr_dev_inst *sdi,
        uint8_t *data, size_t length, size_t sample_width)
 {
        const struct sr_datafeed_logic logic = {
@@ -409,13 +411,13 @@ SR_PRIV void la_send_data_proc(struct sr_dev_inst *sdi,
        sr_session_send(sdi, &packet);
 }
 
-SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transfer)
+static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
 {
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
        gboolean packet_has_error = FALSE;
        unsigned int num_samples;
-       int trigger_offset, cur_sample_count, unitsize;
+       int trigger_offset, cur_sample_count, unitsize, processed_samples;
        int pre_trigger_samples;
 
        sdi = transfer->user_data;
@@ -436,6 +438,7 @@ SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transf
        /* Save incoming transfer before reusing the transfer struct. */
        unitsize = devc->sample_wide ? 2 : 1;
        cur_sample_count = transfer->actual_length / unitsize;
+       processed_samples = 0;
 
        switch (transfer->status) {
        case LIBUSB_TRANSFER_NO_DEVICE:
@@ -466,50 +469,113 @@ SR_PRIV void LIBUSB_CALL fx2lafw_receive_transfer(struct libusb_transfer *transf
        } else {
                devc->empty_transfer_count = 0;
        }
+
+check_trigger:
        if (devc->trigger_fired) {
                if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) {
                        /* Send the incoming transfer to the session bus. */
-                       if (devc->limit_samples && devc->sent_samples + cur_sample_count > devc->limit_samples)
+                       num_samples = cur_sample_count - processed_samples;
+                       if (devc->limit_samples && devc->sent_samples + num_samples > devc->limit_samples)
                                num_samples = devc->limit_samples - devc->sent_samples;
-                       else
-                               num_samples = cur_sample_count;
 
-                       devc->send_data_proc(sdi, (uint8_t *)transfer->buffer,
+                       devc->send_data_proc(sdi, (uint8_t *)transfer->buffer + processed_samples * unitsize,
                                num_samples * unitsize, unitsize);
                        devc->sent_samples += num_samples;
+                       processed_samples += num_samples;
                }
        } else {
                trigger_offset = soft_trigger_logic_check(devc->stl,
-                       transfer->buffer, transfer->actual_length, &pre_trigger_samples);
+                       transfer->buffer + processed_samples * unitsize,
+                       transfer->actual_length - processed_samples * unitsize,
+                       &pre_trigger_samples);
                if (trigger_offset > -1) {
+                       std_session_send_df_frame_begin(sdi);
                        devc->sent_samples += pre_trigger_samples;
-                       num_samples = cur_sample_count - trigger_offset;
+                       num_samples = cur_sample_count - processed_samples - trigger_offset;
                        if (devc->limit_samples &&
-                                       num_samples > devc->limit_samples - devc->sent_samples)
+                                       devc->sent_samples + num_samples > devc->limit_samples)
                                num_samples = devc->limit_samples - devc->sent_samples;
 
                        devc->send_data_proc(sdi, (uint8_t *)transfer->buffer
+                                       + processed_samples * unitsize
                                        + trigger_offset * unitsize,
                                        num_samples * unitsize, unitsize);
                        devc->sent_samples += num_samples;
+                       processed_samples += trigger_offset + num_samples;
 
                        devc->trigger_fired = TRUE;
                }
        }
 
-       if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) {
+       const int frame_ended = devc->limit_samples && (devc->sent_samples >= devc->limit_samples);
+       const int final_frame = devc->limit_frames && (devc->num_frames >= (devc->limit_frames - 1));
+
+       if (frame_ended) {
+               devc->num_frames++;
+               devc->sent_samples = 0;
+               devc->trigger_fired = FALSE;
+               std_session_send_df_frame_end(sdi);
+
+               /* There may be another trigger in the remaining data, go back and check for it */
+               if (processed_samples < cur_sample_count) {
+                       /* Reset the trigger stage */
+                       if (devc->stl)
+                               devc->stl->cur_stage = 0;
+                       else {
+                               std_session_send_df_frame_begin(sdi);
+                               devc->trigger_fired = TRUE;
+                       }
+                       if (!final_frame)
+                               goto check_trigger;
+               }
+       }
+       if (frame_ended && final_frame) {
                fx2lafw_abort_acquisition(devc);
                free_transfer(transfer);
        } else
                resubmit_transfer(transfer);
 }
 
+static int configure_channels(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       const GSList *l;
+       int p;
+       struct sr_channel *ch;
+       uint32_t channel_mask = 0, num_analog = 0;
+
+       devc = sdi->priv;
+
+       g_slist_free(devc->enabled_analog_channels);
+       devc->enabled_analog_channels = NULL;
+
+       for (l = sdi->channels, p = 0; l; l = l->next, p++) {
+               ch = l->data;
+               if ((p <= NUM_CHANNELS) && (ch->type == SR_CHANNEL_ANALOG)
+                               && (ch->enabled)) {
+                       num_analog++;
+                       devc->enabled_analog_channels =
+                           g_slist_append(devc->enabled_analog_channels, ch);
+               } else {
+                       channel_mask |= ch->enabled << p;
+               }
+       }
+
+       /*
+        * Use wide sampling if either any of the LA channels 8..15 is enabled,
+        * and/or at least one analog channel is enabled.
+        */
+       devc->sample_wide = channel_mask > 0xff || num_analog > 0;
+
+       return SR_OK;
+}
+
 static unsigned int to_bytes_per_ms(unsigned int samplerate)
 {
        return samplerate / 1000;
 }
 
-SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc)
+static size_t get_buffer_size(struct dev_context *devc)
 {
        size_t s;
 
@@ -521,13 +587,13 @@ SR_PRIV size_t fx2lafw_get_buffer_size(struct dev_context *devc)
        return (s + 511) & ~511;
 }
 
-SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc)
+static unsigned int get_number_of_transfers(struct dev_context *devc)
 {
        unsigned int n;
 
        /* Total buffer size should be able to hold about 500ms of data. */
        n = (500 * to_bytes_per_ms(devc->cur_samplerate) /
-               fx2lafw_get_buffer_size(devc));
+               get_buffer_size(devc));
 
        if (n > NUM_SIMUL_TRANSFERS)
                return NUM_SIMUL_TRANSFERS;
@@ -535,13 +601,153 @@ SR_PRIV unsigned int fx2lafw_get_number_of_transfers(struct dev_context *devc)
        return n;
 }
 
-SR_PRIV unsigned int fx2lafw_get_timeout(struct dev_context *devc)
+static unsigned int get_timeout(struct dev_context *devc)
 {
        size_t total_size;
        unsigned int timeout;
 
-       total_size = fx2lafw_get_buffer_size(devc) *
-                       fx2lafw_get_number_of_transfers(devc);
+       total_size = get_buffer_size(devc) *
+                       get_number_of_transfers(devc);
        timeout = total_size / to_bytes_per_ms(devc->cur_samplerate);
        return timeout + timeout / 4; /* Leave a headroom of 25% percent. */
 }
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+       struct timeval tv;
+       struct drv_context *drvc;
+
+       (void)fd;
+       (void)revents;
+
+       drvc = (struct drv_context *)cb_data;
+
+       tv.tv_sec = tv.tv_usec = 0;
+       libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+       return TRUE;
+}
+
+static int start_transfers(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       struct sr_trigger *trigger;
+       struct libusb_transfer *transfer;
+       unsigned int i, num_transfers;
+       int timeout, ret;
+       unsigned char *buf;
+       size_t size;
+
+       devc = sdi->priv;
+       usb = sdi->conn;
+
+       devc->sent_samples = 0;
+       devc->acq_aborted = FALSE;
+       devc->empty_transfer_count = 0;
+
+       if ((trigger = sr_session_trigger_get(sdi->session))) {
+               int pre_trigger_samples = 0;
+               if (devc->limit_samples > 0)
+                       pre_trigger_samples = (devc->capture_ratio * devc->limit_samples) / 100;
+               devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
+               if (!devc->stl)
+                       return SR_ERR_MALLOC;
+               devc->trigger_fired = FALSE;
+       } else {
+               std_session_send_df_frame_begin(sdi);
+               devc->trigger_fired = TRUE;
+       }
+
+       num_transfers = get_number_of_transfers(devc);
+
+       size = get_buffer_size(devc);
+       devc->submitted_transfers = 0;
+
+       devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
+       if (!devc->transfers) {
+               sr_err("USB transfers malloc failed.");
+               return SR_ERR_MALLOC;
+       }
+
+       timeout = get_timeout(devc);
+       devc->num_transfers = num_transfers;
+       for (i = 0; i < num_transfers; i++) {
+               if (!(buf = g_try_malloc(size))) {
+                       sr_err("USB transfer buffer malloc failed.");
+                       return SR_ERR_MALLOC;
+               }
+               transfer = libusb_alloc_transfer(0);
+               libusb_fill_bulk_transfer(transfer, usb->devhdl,
+                               2 | LIBUSB_ENDPOINT_IN, buf, size,
+                               receive_transfer, (void *)sdi, timeout);
+               sr_info("submitting transfer: %d", i);
+               if ((ret = libusb_submit_transfer(transfer)) != 0) {
+                       sr_err("Failed to submit transfer: %s.",
+                              libusb_error_name(ret));
+                       libusb_free_transfer(transfer);
+                       g_free(buf);
+                       fx2lafw_abort_acquisition(devc);
+                       return SR_ERR;
+               }
+               devc->transfers[i] = transfer;
+               devc->submitted_transfers++;
+       }
+
+       /*
+        * If this device has analog channels and at least one of them is
+        * enabled, use mso_send_data_proc() to properly handle the analog
+        * data. Otherwise use la_send_data_proc().
+        */
+       if (g_slist_length(devc->enabled_analog_channels) > 0)
+               devc->send_data_proc = mso_send_data_proc;
+       else
+               devc->send_data_proc = la_send_data_proc;
+
+       std_session_send_df_header(sdi);
+
+       return SR_OK;
+}
+
+SR_PRIV int fx2lafw_start_acquisition(const struct sr_dev_inst *sdi)
+{
+       struct sr_dev_driver *di;
+       struct drv_context *drvc;
+       struct dev_context *devc;
+       int timeout, ret;
+       size_t size;
+
+       di = sdi->driver;
+       drvc = di->context;
+       devc = sdi->priv;
+
+       devc->ctx = drvc->sr_ctx;
+       devc->num_frames = 0;
+       devc->sent_samples = 0;
+       devc->empty_transfer_count = 0;
+       devc->acq_aborted = FALSE;
+
+       if (configure_channels(sdi) != SR_OK) {
+               sr_err("Failed to configure channels.");
+               return SR_ERR;
+       }
+
+       timeout = get_timeout(devc);
+       usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc);
+
+       size = get_buffer_size(devc);
+       /* Prepare for analog sampling. */
+       if (g_slist_length(devc->enabled_analog_channels) > 0) {
+               /* We need a buffer half the size of a transfer. */
+               devc->logic_buffer = g_try_malloc(size / 2);
+               devc->analog_buffer = g_try_malloc(
+                       sizeof(float) * size / 2);
+       }
+       start_transfers(sdi);
+       if ((ret = command_start_acquisition(sdi)) != SR_OK) {
+               fx2lafw_abort_acquisition(devc);
+               return ret;
+       }
+
+       return SR_OK;
+}