+/*
+ * A chunk (received via USB) contains a number of transfers (USB length
+ * divided by 16) which contain a number of packets (5 per transfer) which
+ * contain a number of samples (8bit repeat count per 16bit sample data).
+ */
+static void send_chunk(struct sr_dev_inst *sdi,
+ const uint8_t *packets, size_t num_xfers)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_logic logic;
+ struct sr_datafeed_packet sr_packet;
+ unsigned int max_samples, n_samples, total_samples, free_n_samples;
+ size_t num_pkts;
+ gboolean do_signal_trigger;
+ uint8_t *wp;
+ const uint8_t *rp;
+ uint16_t sample_value;
+ size_t repetitions;
+ uint8_t sample_buff[sizeof(sample_value)];
+
+ devc = sdi->priv;
+
+ logic.unitsize = sizeof(sample_buff);
+ logic.data = devc->convbuffer;
+
+ sr_packet.type = SR_DF_LOGIC;
+ sr_packet.payload = &logic;
+
+ max_samples = devc->convbuffer_size / sizeof(sample_buff);
+ n_samples = 0;
+ wp = devc->convbuffer;
+ total_samples = 0;
+ do_signal_trigger = FALSE;
+
+ if (devc->trigger_involved && !devc->trigger_marked && devc->info.n_rep_packets_before_trigger == 0) {
+ std_session_send_df_trigger(sdi);
+ devc->trigger_marked = TRUE;
+ }
+
+ rp = packets;
+ while (num_xfers--) {
+ num_pkts = NUM_PACKETS_IN_CHUNK;
+ while (num_pkts--) {
+ /*
+ * Flush the conversion buffer when a trigger
+ * location needs to get communicated, or when
+ * an to-get-expected sample repetition count
+ * would no longer fit into the buffer.
+ */
+ free_n_samples = max_samples - n_samples;
+ if (free_n_samples < 256 || do_signal_trigger) {
+ logic.length = n_samples * sizeof(sample_buff);;
+ sr_session_send(sdi, &sr_packet);
+ n_samples = 0;
+ wp = devc->convbuffer;
+ if (do_signal_trigger) {
+ std_session_send_df_trigger(sdi);
+ do_signal_trigger = FALSE;
+ }
+ }
+
+ sample_value = read_u16le_inc(&rp);
+ repetitions = read_u8_inc(&rp);
+
+ n_samples += repetitions;
+ total_samples += repetitions;
+ devc->total_samples += repetitions;
+
+ write_u16le(sample_buff, sample_value);
+ while (repetitions--) {
+ memcpy(wp, sample_buff, logic.unitsize);
+ wp += logic.unitsize;
+ }
+
+ if (devc->trigger_involved && !devc->trigger_marked) {
+ if (!--devc->n_reps_until_trigger) {
+ devc->trigger_marked = TRUE;
+ do_signal_trigger = TRUE;
+ sr_dbg("Trigger position after %" PRIu64 " samples, %.6fms.",
+ devc->total_samples,
+ (double)devc->total_samples / devc->cur_samplerate * 1e3);
+ }
+ }
+ }
+ (void)read_u8_inc(&rp); /* Skip sequence number. */
+ }
+ if (n_samples) {
+ logic.length = n_samples * logic.unitsize;
+ sr_session_send(sdi, &sr_packet);
+ if (do_signal_trigger) {
+ std_session_send_df_trigger(sdi);
+ }
+ }
+ sr_dbg("Send_chunk done after %u samples.", total_samples);
+}
+
+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;
+ int ret;
+
+ sdi = transfer->user_data;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ sr_dbg("receive_transfer(): status %s received %d bytes.",
+ libusb_error_name(transfer->status), transfer->actual_length);
+
+ if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
+ sr_err("USB bulk transfer timeout.");
+ devc->download_finished = TRUE;
+ }
+ send_chunk(sdi, transfer->buffer, transfer->actual_length / TRANSFER_PACKET_LENGTH);
+
+ devc->n_bytes_to_read -= transfer->actual_length;
+ if (devc->n_bytes_to_read) {
+ uint32_t to_read = devc->n_bytes_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.
+ */
+ if (to_read >= LA2016_USB_BUFSZ)
+ to_read = LA2016_USB_BUFSZ;
+ else
+ to_read = (to_read + (LA2016_EP6_PKTSZ-1)) & ~(LA2016_EP6_PKTSZ-1);
+ 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)
+ return;
+ sr_err("Cannot submit another USB transfer: %s.",
+ libusb_error_name(ret));
+ }
+
+ g_free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ devc->download_finished = TRUE;
+}
+
+SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data)
+{
+ const struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct timeval tv;
+
+ (void)fd;
+ (void)revents;
+
+ sdi = cb_data;
+ devc = sdi->priv;
+ drvc = sdi->driver->context;
+
+ if (!devc->completion_seen) {
+ if (!la2016_is_idle(sdi)) {
+ /* Not yet ready for sample data download. */
+ return TRUE;
+ }
+ devc->completion_seen = TRUE;
+ devc->download_finished = FALSE;
+ devc->trigger_marked = FALSE;
+ devc->total_samples = 0;
+ /* We can start downloading sample data. */
+ if (la2016_start_download(sdi, receive_transfer) != SR_OK) {
+ sr_err("Cannot start acquisition data download.");
+ return FALSE;
+ }
+ sr_dbg("Acquisition data download started.");
+ std_session_send_df_frame_begin(sdi);
+
+ return TRUE;
+ }
+
+ tv.tv_sec = tv.tv_usec = 0;
+ libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
+
+ if (devc->download_finished) {
+ sr_dbg("Download finished, post processing.");
+ std_session_send_df_frame_end(sdi);
+
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+ std_session_send_df_end(sdi);
+
+ la2016_stop_acquisition(sdi);
+
+ g_free(devc->convbuffer);
+ devc->convbuffer = NULL;
+
+ devc->transfer = NULL;
+
+ sr_dbg("Download finished, done post processing.");
+ }
+
+ return TRUE;
+}
+