+ struct timeval tv;
+
+ /* Avoid compiler warnings. */
+ (void)fd;
+ (void)revents;
+ (void)cb_data;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(usb_context, &tv);
+
+ return TRUE;
+}
+
+static void abort_acquisition(struct context *ctx)
+{
+ int i;
+
+ ctx->num_samples = -1;
+
+ for (i = ctx->num_transfers - 1; i >= 0; i--) {
+ if (ctx->transfers[i])
+ libusb_cancel_transfer(ctx->transfers[i]);
+ }
+}
+
+static void finish_acquisition(struct context *ctx)
+{
+ struct sr_datafeed_packet packet;
+ int i;
+
+ /* Terminate session */
+ packet.type = SR_DF_END;
+ sr_session_send(ctx->session_dev_id, &packet);
+
+ /* Remove fds from polling */
+ const struct libusb_pollfd **const lupfd =
+ libusb_get_pollfds(usb_context);
+ for (i = 0; lupfd[i]; i++)
+ sr_source_remove(lupfd[i]->fd);
+ free(lupfd); /* NOT g_free()! */
+
+ ctx->num_transfers = 0;
+ g_free(ctx->transfers);
+}
+
+static void free_transfer(struct libusb_transfer *transfer)
+{
+ struct context *ctx = transfer->user_data;
+ unsigned int i;
+
+ g_free(transfer->buffer);
+ transfer->buffer = NULL;
+ libusb_free_transfer(transfer);
+
+ for (i = 0; i < ctx->num_transfers; i++) {
+ if (ctx->transfers[i] == transfer) {
+ ctx->transfers[i] = NULL;
+ break;
+ }
+ }
+
+ ctx->submitted_transfers--;
+ if (ctx->submitted_transfers == 0)
+ finish_acquisition(ctx);
+
+}
+
+static void resubmit_transfer(struct libusb_transfer *transfer)
+{
+ if (libusb_submit_transfer(transfer) != 0) {
+ free_transfer(transfer);
+ /* TODO: Stop session? */
+ /* TODO: Better error message. */
+ sr_err("fx2lafw: %s: libusb_submit_transfer error.", __func__);
+ }
+}
+
+static void receive_transfer(struct libusb_transfer *transfer)
+{
+ gboolean packet_has_error = FALSE;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ struct context *ctx = transfer->user_data;
+ int trigger_offset, i;
+
+ /*
+ * If acquisition has already ended, just free any queued up
+ * transfer that come in.
+ */
+ if (ctx->num_samples == -1) {
+ free_transfer(transfer);
+ return;
+ }
+
+ sr_info("fx2lafw: receive_transfer(): status %d received %d bytes.",
+ transfer->status, transfer->actual_length);
+
+ /* Save incoming transfer before reusing the transfer struct. */
+ uint8_t *const cur_buf = transfer->buffer;
+ const int sample_width = ctx->sample_wide ? 2 : 1;
+ const int cur_sample_count = transfer->actual_length / sample_width;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ abort_acquisition(ctx);
+ free_transfer(transfer);
+ return;
+ case LIBUSB_TRANSFER_COMPLETED:
+ case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */
+ break;
+ default:
+ packet_has_error = TRUE;
+ break;
+ }
+
+ if (transfer->actual_length == 0 || packet_has_error) {
+ ctx->empty_transfer_count++;
+ if (ctx->empty_transfer_count > MAX_EMPTY_TRANSFERS) {
+ /*
+ * The FX2 gave up. End the acquisition, the frontend
+ * will work out that the samplecount is short.
+ */
+ abort_acquisition(ctx);
+ free_transfer(transfer);
+ } else {
+ resubmit_transfer(transfer);
+ }
+ return;
+ } else {
+ ctx->empty_transfer_count = 0;
+ }
+
+ trigger_offset = 0;
+ if (ctx->trigger_stage >= 0) {
+ for (i = 0; i < cur_sample_count; i++) {
+
+ const uint16_t cur_sample = ctx->sample_wide ?
+ *((const uint16_t*)cur_buf + i) :
+ *((const uint8_t*)cur_buf + i);
+
+ if ((cur_sample & ctx->trigger_mask[ctx->trigger_stage]) ==
+ ctx->trigger_value[ctx->trigger_stage]) {
+ /* Match on this trigger stage. */
+ ctx->trigger_buffer[ctx->trigger_stage] = cur_sample;
+ ctx->trigger_stage++;
+
+ if (ctx->trigger_stage == NUM_TRIGGER_STAGES ||
+ ctx->trigger_mask[ctx->trigger_stage] == 0) {
+ /* Match on all trigger stages, we're done. */
+ trigger_offset = i + 1;
+
+ /*
+ * TODO: Send pre-trigger buffer to session bus.
+ * Tell the frontend we hit the trigger here.
+ */
+ packet.type = SR_DF_TRIGGER;
+ packet.payload = NULL;
+ sr_session_send(ctx->session_dev_id, &packet);
+
+ /*
+ * Send the samples that triggered it, since we're
+ * skipping past them.
+ */
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.unitsize = sizeof(*ctx->trigger_buffer);
+ logic.length = ctx->trigger_stage * logic.unitsize;
+ logic.data = ctx->trigger_buffer;
+ sr_session_send(ctx->session_dev_id, &packet);
+
+ ctx->trigger_stage = TRIGGER_FIRED;
+ break;
+ }
+ } else if (ctx->trigger_stage > 0) {
+ /*
+ * We had a match before, but not in the next sample. However, we may
+ * have a match on this stage in the next bit -- trigger on 0001 will
+ * fail on seeing 00001, so we need to go back to stage 0 -- but at
+ * the next sample from the one that matched originally, which the
+ * counter increment at the end of the loop takes care of.
+ */
+ i -= ctx->trigger_stage;
+ if (i < -1)
+ i = -1; /* Oops, went back past this buffer. */
+ /* Reset trigger stage. */
+ ctx->trigger_stage = 0;
+ }
+ }
+ }
+
+ if (ctx->trigger_stage == TRIGGER_FIRED) {
+ /* Send the incoming transfer to the session bus. */
+ const int trigger_offset_bytes = trigger_offset * sample_width;
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+ logic.length = transfer->actual_length - trigger_offset_bytes;
+ logic.unitsize = sample_width;
+ logic.data = cur_buf + trigger_offset_bytes;
+ sr_session_send(ctx->session_dev_id, &packet);
+
+ ctx->num_samples += cur_sample_count;
+ if (ctx->limit_samples &&
+ (unsigned int)ctx->num_samples > ctx->limit_samples) {
+ abort_acquisition(ctx);
+ free_transfer(transfer);
+ return;
+ }
+ } else {
+ /*
+ * TODO: Buffer pre-trigger data in capture
+ * ratio-sized buffer.
+ */
+ }
+
+ resubmit_transfer(transfer);
+}
+
+static unsigned int to_bytes_per_ms(unsigned int samplerate)
+{
+ return samplerate / 1000;
+}
+
+static size_t get_buffer_size(struct context *ctx)
+{
+ size_t s;
+
+ /* The buffer should be large enough to hold 10ms of data and a multiple
+ * of 512. */
+ s = 10 * to_bytes_per_ms(ctx->cur_samplerate);
+ return (s + 511) & ~511;
+}
+
+static unsigned int get_number_of_transfers(struct context *ctx)
+{
+ unsigned int n;
+
+ /* Total buffer size should be able to hold about 500ms of data */
+ n = 500 * to_bytes_per_ms(ctx->cur_samplerate) / get_buffer_size(ctx);
+
+ if (n > NUM_SIMUL_TRANSFERS)
+ return NUM_SIMUL_TRANSFERS;
+
+ return n;
+}
+
+static unsigned int get_timeout(struct context *ctx)
+{
+ size_t total_size;
+ unsigned int timeout;
+
+ total_size = get_buffer_size(ctx) * get_number_of_transfers(ctx);
+ timeout = total_size / to_bytes_per_ms(ctx->cur_samplerate);
+ return timeout + timeout / 4; /* Leave a headroom of 25% percent */
+}
+
+static int hw_dev_acquisition_start(int dev_index, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_header header;
+ struct sr_datafeed_meta_logic meta;
+ struct context *ctx;
+ struct libusb_transfer *transfer;
+ const struct libusb_pollfd **lupfd;
+ unsigned int i;
+ int ret;
+ unsigned char *buf;
+
+ if (!(sdi = sr_dev_inst_get(dev_insts, dev_index)))
+ return SR_ERR;
+ ctx = sdi->priv;
+
+ if (ctx->submitted_transfers != 0)
+ return SR_ERR;
+
+ ctx->session_dev_id = cb_data;
+ ctx->num_samples = 0;
+ ctx->empty_transfer_count = 0;
+
+ const unsigned int timeout = get_timeout(ctx);
+ const unsigned int num_transfers = get_number_of_transfers(ctx);
+ const size_t size = get_buffer_size(ctx);
+
+ ctx->transfers = g_try_malloc0(sizeof(*ctx->transfers) * num_transfers);
+ if (!ctx->transfers)
+ return SR_ERR;
+
+ ctx->num_transfers = num_transfers;
+
+ for (i = 0; i < num_transfers; i++) {
+ if (!(buf = g_try_malloc(size))) {
+ sr_err("fx2lafw: %s: buf malloc failed.", __func__);
+ return SR_ERR_MALLOC;
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, ctx->usb->devhdl,
+ 2 | LIBUSB_ENDPOINT_IN, buf, size,
+ receive_transfer, ctx, timeout);
+ if (libusb_submit_transfer(transfer) != 0) {
+ libusb_free_transfer(transfer);
+ g_free(buf);
+ abort_acquisition(ctx);
+ return SR_ERR;
+ }
+ ctx->transfers[i] = transfer;
+ ctx->submitted_transfers++;
+ }
+
+ lupfd = libusb_get_pollfds(usb_context);
+ for (i = 0; lupfd[i]; i++)
+ sr_source_add(lupfd[i]->fd, lupfd[i]->events,
+ timeout, receive_data, NULL);
+ free(lupfd); /* NOT g_free()! */
+
+ packet.type = SR_DF_HEADER;
+ packet.payload = &header;
+ header.feed_version = 1;
+ gettimeofday(&header.starttime, NULL);
+ sr_session_send(cb_data, &packet);
+
+ /* Send metadata about the SR_DF_LOGIC packets to come. */
+ packet.type = SR_DF_META_LOGIC;
+ packet.payload = &meta;
+ meta.samplerate = ctx->cur_samplerate;
+ meta.num_probes = ctx->sample_wide ? 16 : 8;
+ sr_session_send(cb_data, &packet);
+
+ if ((ret = command_start_acquisition (ctx->usb->devhdl,
+ ctx->cur_samplerate, ctx->sample_wide)) != SR_OK) {
+ abort_acquisition(ctx);
+ return ret;
+ }
+