Hallo,
with appended patch and the two memory leak patches I have read 10's of
Gigabyte of data at about 13.5 MByte/sec without any skipped data and any
leaked memory.
Thomas, is the possible clock skip still an issue?
Bye
--
Uwe Bonnes bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Institut fuer Kernphysik Schlossgartenstrasse 9 64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------
>From 35dae19739b1df5fecccf232eeafa04056f9136f Mon Sep 17 00:00:00 2001
From: Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 8 Jun 2010 13:20:10 +0200
Subject: Streaming read
---
examples/CMakeLists.txt | 2 +
examples/stream_test.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++
src/CMakeLists.txt | 2 +-
src/Makefile.am | 2 +-
src/ftdi.h | 23 +++
src/ftdi_stream.c | 275 +++++++++++++++++++++++++++++++++++++
6 files changed, 653 insertions(+), 2 deletions(-)
create mode 100644 examples/stream_test.c
create mode 100644 src/ftdi_stream.c
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index d2aeecb..7b702c2 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -20,6 +20,7 @@ if (EXAMPLES)
add_executable(find_all find_all.c)
add_executable(serial_read serial_read.c)
add_executable(baud_test baud_test.c)
+ add_executable(stream_test stream_test.c)
# Linkage
target_link_libraries(simple ftdi)
@@ -30,6 +31,7 @@ if (EXAMPLES)
target_link_libraries(find_all ftdi)
target_link_libraries(serial_read ftdi)
target_link_libraries(baud_test ftdi)
+ target_link_libraries(stream_test ftdi)
# libftdi++ examples
if(FTDI_BUILD_CPP)
diff --git a/examples/stream_test.c b/examples/stream_test.c
new file mode 100644
index 0000000..0025435
--- /dev/null
+++ b/examples/stream_test.c
@@ -0,0 +1,351 @@
+/* stream_test.c
+ *
+ * Test reading from FT2232H in synchronous FIFO mode.
+ *
+ * The FT2232H must supply data due to an appropriate circuit
+ *
+ * To check for skipped block with appended code,
+ * a structure as follows is assumed
+ * 1* uint32_t num (incremented in 0x4000 steps)
+ * 3* uint32_t dont_care
+ *
+ * After start, data will be read in streaming until the program is aborted
+ * Progess information wil be printed out
+ * If a filename is given on the command line, the data read will be
+ * written to that file
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <ftdi.h>
+void check_outfile(char *);
+
+static FILE *outputFile;
+
+static int check = 1;
+static int exitRequested;
+/*
+ * sigintHandler --
+ *
+ * SIGINT handler, so we can gracefully exit when the user hits ctrl-C.
+ */
+
+static void
+sigintHandler(int signum)
+{
+ exitRequested = 1;
+}
+
+static void
+usage(const char *argv0)
+{
+ fprintf(stderr,
+ "Usage: %s [tions...] \n"
+ "Test streaming read from FT2232H\n"
+ "[-P string] only look for product with given string"
+ "[-n] don't check for special block structure"
+ "\n"
+ "If some filename is given, write data read to that file\n"
+ "Progess information is printed each second\n"
+ "Abort with ^C\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ "Copyright (C) 2009 Micah Dowty <micah@xxxxxxx>\n",
+ "Adapted for use with libftdi (C) 2010 Uwe Bonnes
<bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>\n",
+ argv0);
+ exit(1);
+}
+
+static uint32_t start = 0;
+static uint32_t offset = 0;
+static uint64_t blocks = 0;
+static uint32_t skips = 0;
+static uint32_t n_err = 0;
+static int
+readCallback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void
*userdata)
+{
+ if (length)
+ {
+ if (check)
+ {
+ int i,rem;
+ uint32_t num;
+ for (i= offset; i<length-16; i+=16)
+ {
+ num = *(uint32_t*) (buffer+i);
+ if (start && (num != start +0x4000))
+ {
+ uint32_t delta = ((num-start)/0x4000)-1;
+ fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at
blocks %10ld \n",
+ delta, start -0x4000, num, blocks);
+ n_err++;
+ skips += delta;
+ }
+ blocks ++;
+ start = num;
+ }
+ rem = length -i;
+ if (rem >3)
+ {
+ num = *(uint32_t*) (buffer+i);
+ if (start && (num != start +0x4000))
+ {
+ uint32_t delta = ((num-start)/0x4000)-1;
+ fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at
blocks %10ld \n",
+ delta, start -0x4000, num, blocks);
+ n_err++;
+ skips += delta;
+ }
+ start = num;
+ }
+ else if (rem)
+ start += 0x4000;
+ if (rem != 0);
+ {
+ blocks ++;
+ offset = 16-rem;
+ }
+ }
+ if (outputFile)
+ {
+ if (fwrite(buffer, length, 1, outputFile) != 1)
+ {
+ perror("Write error");
+ return 1;
+ }
+ }
+ }
+ if (progress)
+ {
+ fprintf(stderr, "%10.02fs total time %9.3f MiB captured %7.1f kB/s curr
rate %7.1f kB/s totalrate %5d skips\n",
+ progress->totalTime,
+ progress->current.totalBytes / (1024.0 * 1024.0),
+ progress->currentRate / 1024.0,
+ progress->totalRate / 1024.0, skips);
+ }
+ return exitRequested ? 1 : 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct ftdi_context ftdic;
+ int err, c;
+ FILE *of = NULL;
+ char const *outfile = 0;
+ outputFile =0;
+ exitRequested = 0;
+ char *descstring = NULL;
+ int option_index;
+ static struct option long_options[] = {{NULL},};
+
+ while ((c = getopt_long(argc, argv, "P:n", long_options, &option_index))
!=- 1)
+ switch (c)
+ {
+ case -1:
+ break;
+ case 'P':
+ descstring = optarg;
+ break;
+ case 'n':
+ check = 0;
+ break;
+ default:
+ usage(argv[0]);
+ }
+
+ if (optind == argc - 1)
+ {
+ // Exactly one extra argument- a dump file
+ outfile = argv[optind];
+ }
+ else if (optind < argc)
+ {
+ // Too many extra args
+ usage(argv[0]);
+ }
+
+ if (ftdi_init(&ftdic) < 0)
+ {
+ fprintf(stderr, "ftdi_init failed\n");
+ return EXIT_FAILURE;
+ }
+
+ if (ftdi_set_interface(&ftdic, INTERFACE_A) < 0)
+ {
+ fprintf(stderr, "ftdi_set_interface failed\n");
+ return EXIT_FAILURE;
+ }
+
+ if (ftdi_usb_open_desc(&ftdic, 0x0403, 0x6010, descstring, NULL) < 0)
+ {
+ fprintf(stderr,"Can't open ftdi device:
%s\n",ftdi_get_error_string(&ftdic));
+ return EXIT_FAILURE;
+ }
+
+ /* A timeout value of 1 results in may skipped blocks */
+ if(ftdi_set_latency_timer(&ftdic, 2))
+ {
+ fprintf(stderr,"Can't set latency\n",ftdi_get_error_string(&ftdic));
+ return EXIT_FAILURE;
+ }
+
+/* if(ftdi_usb_purge_rx_buffer(&ftdic) < 0)
+ {
+ fprintf(stderr,"Can't rx purge\n",ftdi_get_error_string(&ftdic));
+ return EXIT_FAILURE;
+ }*/
+ if (outfile)
+ if ((of = fopen(outfile,"w+")) == 0)
+ fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile,
strerror(errno));
+ if (of)
+ if (setvbuf(of, NULL, _IOFBF , 1<<16) == 0)
+ outputFile = of;
+ signal(SIGINT, sigintHandler);
+
+ err = ftdi_readstream(&ftdic, readCallback, NULL, 8, 256);
+ if (err < 0 && !exitRequested)
+ exit(1);
+
+ if (outputFile) {
+ fclose(outputFile);
+ outputFile = NULL;
+ }
+ fprintf(stderr, "Capture ended.\n");
+
+ if (ftdi_set_bitmode(&ftdic, 0xff, BITMODE_RESET) < 0)
+ {
+ fprintf(stderr,"Can't set synchronous fifo
mode\n",ftdi_get_error_string(&ftdic));
+ return EXIT_FAILURE;
+ }
+ ftdi_usb_close(&ftdic);
+ ftdi_deinit(&ftdic);
+ signal(SIGINT, SIG_DFL);
+ if (check && outfile)
+ {
+ if ((outputFile = fopen(outfile,"r")) == 0)
+ {
+ fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile,
strerror(errno));
+ return EXIT_FAILURE;
+ }
+ check_outfile(descstring);
+ fclose(outputFile);
+ }
+ else if (check)
+ fprintf(stderr,"%d errors of %d blocks (%Le), %d (%Le) blocks
skipped\n",
+ n_err, blocks, (long double)n_err/(long double) blocks,
+ skips, (long double)skips/(long double) blocks);
+ exit (0);
+}
+
+void check_outfile(char *descstring)
+{
+ if(strcmp(descstring,"FT2232HTEST") == 0)
+ {
+ char buf0[1024];
+ char buf1[1024];
+ char bufr[1024];
+ char *pa, *pb, *pc;
+ unsigned int num_lines = 0, line_num = 1;
+ int err_count = 0;
+ unsigned int num_start, num_end;
+
+ unsigned int block[4];
+ pa = buf0;
+ pb = buf1;
+ pc = buf0;
+ if(fgets(pa, 1023, outputFile) == NULL)
+ {
+ fprintf(stderr,"Empty output file\n");
+ return;
+ }
+ while(fgets(pb, 1023, outputFile) != NULL)
+ {
+ num_lines++;
+ unsigned int num_save = num_start;
+ if( sscanf(pa,"%6u%94s%6u",&num_start, bufr,&num_end) !=3)
+ {
+ fprintf(stdout,"Format doesn't match at line %8d \"%s",
+ num_lines, pa);
+ err_count++;
+ line_num = num_save +2;
+ }
+ else
+ {
+ if ((num_start+1)%100000 != num_end)
+ {
+ if (err_count < 20)
+ fprintf(stdout,"Malformed line %d \"%s\"\n",
+ num_lines, pa);
+ err_count++;
+ }
+ else if(num_start != line_num)
+ {
+ if (err_count < 20)
+ fprintf(stdout,"Skipping from %d to %d\n",
+ line_num, num_start);
+ err_count++;
+
+ }
+ line_num = num_end;
+ }
+ pa = pb;
+ pb = pc;
+ pc = pa;
+ }
+ if(err_count)
+ fprintf(stdout,"\n%d errors of %d data sets %f\n", err_count,
num_lines, (double) err_count/(double)num_lines);
+ else
+ fprintf(stdout,"No errors for %d lines\n",num_lines);
+ }
+ else if(strcmp(descstring,"LLBBC10") == 0)
+ {
+ uint32_t block0[4];
+ uint32_t block1[4];
+ uint32_t *pa = block0;
+ uint32_t *pb = block1;
+ uint32_t *pc = block0;
+ uint32_t start= 0;
+ uint32_t nread = 0;
+ int expect = 1;
+ int n_shown = 0;
+ int n_errors = 0;
+ if (fread(pa, sizeof(uint32_t), 4,outputFile) < 4)
+ {
+ fprintf(stderr,"Empty result file\n");
+ return;
+ }
+ while(fread(pb, sizeof(uint32_t), 4,outputFile) != 0)
+ {
+ blocks++;
+ nread = pa[0];
+ if(start>0 && (nread != start))
+ {
+ if(n_shown < 30)
+ {
+ fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at
blocks %10d \n",
+ (nread-start)/0x4000, start -0x4000, nread,
blocks);
+ n_shown ++;
+ }
+ n_errors++;
+ }
+ else if (n_shown >0)
+ n_shown--;
+ start = nread + 0x4000;
+ pa = pb;
+ pb = pc;
+ pc = pa;
+ }
+ if(n_errors)
+ fprintf(stderr, "%d blocks wrong from %d blocks read\n",
+ n_errors, blocks);
+ else
+ fprintf(stderr, "%d blocks all fine\n",blocks);
+ }
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0e12e54..a4da870 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,7 +4,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR}
)
# Targets
-set(c_sources ftdi.c)
+set(c_sources ftdi.c ftdi_stream.c)
set(c_headers ftdi.h)
add_library(ftdi SHARED ${c_sources})
diff --git a/src/Makefile.am b/src/Makefile.am
index 9de0d6d..9141fb9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
# the library search path.
lib_LTLIBRARIES = libftdi.la
-libftdi_la_SOURCES = ftdi.c
+libftdi_la_SOURCES = ftdi.c ftdi_stream.c
include_HEADERS = ftdi.h
# Note: If you specify a:b:c as the version in the next line,
diff --git a/src/ftdi.h b/src/ftdi.h
index 179d3ca..60c6826 100644
--- a/src/ftdi.h
+++ b/src/ftdi.h
@@ -278,6 +278,29 @@ struct ftdi_eeprom
int size;
};
+/**
+ \brief Progress Info for streaming read
+*/
+struct size_and_time
+{
+ uint64_t totalBytes;
+ struct timeval time;
+};
+
+typedef struct
+{
+ struct size_and_time first;
+ struct size_and_time prev;
+ struct size_and_time current;
+ double totalTime;
+ double totalRate;
+ double currentRate;
+} FTDIProgressInfo;
+
+typedef int (FTDIStreamCallback)(uint8_t *buffer, int length,
+ FTDIProgressInfo *progress, void *userdata);
+
+
#ifdef __cplusplus
extern "C"
{
diff --git a/src/ftdi_stream.c b/src/ftdi_stream.c
new file mode 100644
index 0000000..4575f72
--- /dev/null
+++ b/src/ftdi_stream.c
@@ -0,0 +1,275 @@
+/***************************************************************************
+ ftdi_stream.c - description
+ -------------------
+ copyright : (C) 2009 Micah Dowty 2010 Uwe Bonnes
+ email : opensource@xxxxxxxxxxxxx
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License *
+ * version 2.1 as published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+/* Adapted from
+ * fastftdi.c - A minimal FTDI FT232H interface for which supports bit-bang
+ * mode, but focuses on very high-performance support for
+ * synchronous FIFO mode. Requires libusb-1.0
+ *
+ * Copyright (C) 2009 Micah Dowty
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "ftdi.h"
+
+typedef struct
+{
+ FTDIStreamCallback *callback;
+ void *userdata;
+ int packetsize;
+ int activity;
+ int result;
+ FTDIProgressInfo progress;
+} FTDIStreamState;
+
+/* Handle callbacks
+ *
+ * With Exit request, free memory and release the transfer
+ *
+ * state->result is only set when some error happens
+ */
+static void
+ftdi_readstream_cb(struct libusb_transfer *transfer)
+{
+ FTDIStreamState *state = transfer->user_data;
+ int packet_size = state->packetsize;
+
+ state->activity++;
+ if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
+ {
+ int i;
+ uint8_t *ptr = transfer->buffer;
+ int length = transfer->actual_length;
+ int numPackets = (length + packet_size - 1) / packet_size;
+ int res;
+
+ for (i = 0; i < numPackets; i++)
+ {
+ int payloadLen;
+ int packetLen = length;
+
+ if (packetLen > packet_size)
+ packetLen = packet_size;
+
+ payloadLen = packetLen - 2;
+ state->progress.current.totalBytes += payloadLen;
+
+ res = state->callback(ptr + 2, payloadLen,
+ NULL, state->userdata);
+
+ ptr += packetLen;
+ length -= packetLen;
+ }
+ if (res)
+ {
+ free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ }
+ else
+ {
+ transfer->status = -1;
+ state->result = libusb_submit_transfer(transfer);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "unknown status %d\n",transfer->status);
+ state->result = LIBUSB_ERROR_IO;
+ }
+}
+
+/**
+ Helper function to calculate (unix) time differences
+
+ \param a timeval
+ \param b timeval
+*/
+static double
+TimevalDiff(const struct timeval *a, const struct timeval *b)
+{
+ return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec);
+}
+
+/**
+ Streaming reading of data from the device
+
+ Use asynchronous transfers in libusb-1.0 for high-performance
+ streaming of data from a device interface back to the PC. This
+ function continuously transfers data until either an error occurs
+ or the callback returns a nonzero value. This function returns
+ a libusb error code or the callback's return value.
+
+ For every contiguous block of received data, the callback will
+ be invoked.
+
+ \param ftdi pointer to ftdi_context
+ \param callback to user supplied function for one block of data
+ \param userdata
+ \param packetsPerTransfer number of packets per transfer
+ \param numTransfers Number of transfers per callback
+
+*/
+
+int
+ftdi_readstream(struct ftdi_context *ftdi,
+ FTDIStreamCallback *callback, void *userdata,
+ int packetsPerTransfer, int numTransfers)
+{
+ struct libusb_transfer **transfers;
+ FTDIStreamState state = { callback, userdata, ftdi->max_packet_size, 1 };
+ int bufferSize = packetsPerTransfer * ftdi->max_packet_size;
+ int xferIndex;
+ int err = 0;
+
+ fprintf(stderr, "ftdi_readstream\n");
+ /*
+ * Set up all transfers
+ */
+
+ transfers = calloc(numTransfers, sizeof *transfers);
+ if (!transfers) {
+ err = LIBUSB_ERROR_NO_MEM;
+ goto cleanup;
+ }
+
+ for (xferIndex = 0; xferIndex < numTransfers; xferIndex++)
+ {
+ struct libusb_transfer *transfer;
+
+ transfer = libusb_alloc_transfer(0);
+ transfers[xferIndex] = transfer;
+ if (!transfer) {
+ err = LIBUSB_ERROR_NO_MEM;
+ goto cleanup;
+ }
+
+ libusb_fill_bulk_transfer(transfer, ftdi->usb_dev, ftdi->out_ep,
+ malloc(bufferSize), bufferSize,
+ ftdi_readstream_cb,
+ &state, 0);
+
+ if (!transfer->buffer) {
+ err = LIBUSB_ERROR_NO_MEM;
+ goto cleanup;
+ }
+
+ transfer->status = -1;
+ err = libusb_submit_transfer(transfer);
+ if (err)
+ goto cleanup;
+ }
+
+ /* Start the transfers only when everything has been set up.
+ * Otherwise the transfers start stuttering and the PC not
+ * fetching data for several to several ten milliseconds
+ * and we skip blocks
+ */
+ if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_SYNCFF) < 0)
+ {
+ fprintf(stderr,"Can't set synchronous fifo mode\n",
+ ftdi_get_error_string(ftdi));
+ goto cleanup;
+ }
+
+ /*
+ * Run the transfers, and periodically assess progress.
+ */
+
+ gettimeofday(&state.progress.first.time, NULL);
+
+ do
+ {
+ FTDIProgressInfo *progress = &state.progress;
+ const double progressInterval = 1.0;
+ struct timeval timeout = { 0, ftdi->usb_read_timeout };
+ struct timeval now;
+
+ int err = libusb_handle_events_timeout(NULL, &timeout);
+ if (err == LIBUSB_ERROR_INTERRUPTED)
+ /* restart interrupted events */
+ err = libusb_handle_events_timeout(NULL, &timeout);
+ if (!state.result)
+ {
+ state.result = err;
+ }
+ if (state.activity == 0)
+ state.result = 1;
+ else
+ state.activity = 0;
+
+ // If enough time has elapsed, update the progress
+ gettimeofday(&now, NULL);
+ if (TimevalDiff(&now, &progress->current.time) >= progressInterval)
+ {
+ progress->current.time = now;
+ progress->totalTime = TimevalDiff(&progress->current.time,
+ &progress->first.time);
+
+ if (progress->prev.totalBytes)
+ {
+ // We have enough information to calculate rates
+
+ double currentTime;
+
+ currentTime = TimevalDiff(&progress->current.time,
+ &progress->prev.time);
+
+ progress->totalRate =
+ progress->current.totalBytes /progress->totalTime;
+ progress->currentRate =
+ (progress->current.totalBytes -
+ progress->prev.totalBytes) / currentTime;
+ }
+
+ state.callback(NULL, 0, progress, state.userdata);
+ progress->prev = progress->current;
+
+ }
+ } while (!state.result);
+
+ /*
+ * Cancel any outstanding transfers, and free memory.
+ */
+
+ cleanup:
+ fprintf(stderr, "cleanup\n");
+ if (transfers)
+ free(transfers);
+ if (err)
+ return err;
+ else
+ return state.result;
+}
+
--
1.6.4.2
--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to libftdi+unsubscribe@xxxxxxxxxxxxxxxxxxxxxxx
|