Hi Uwe,
I would love to see the verilog code it sounds interesting.
Regards, George
On Tue, Apr 27, 2010 at 1:18 PM, Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx> wrote:
Hello,
appended patch adds a streaming read for libftdi-1. It's mostly taken from
Micah Dowty <micahjd@xxxxxxxxx> fastftdi.c|h.
I have a testboard with an FT2232H connected to an Spartan-3, with the
Spartan programmed to start sending some sequence when synchronous fifo mode
is switched on. I can read about 36 MiByte/s with that setup. If the verilog
code is of interest, I can provide to.
The patch adds libusb_context to the ftdi_context structure, but otherwise
touches none of the existing code. libusb_context is needed for handling
libusb_handle_events_timeout().
Feedback welcome.
--
Uwe Bonnes bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Institut fuer Kernphysik Schlossgartenstrasse 9 64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------
>From cfdcda29202875a8327799ef992a160d883569d5 Mon Sep 17 00:00:00 2001
From: Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 27 Apr 2010 19:06:59 +0200
Subject: Add streaming read from Micah Dowty <micahjd@xxxxxxxxx> fastftdi.c
---
CMakeLists.txt | 1 +
examples/CMakeLists.txt | 2 +
examples/stream_test.c | 168 ++++++++++++++++++++++++++++++++++++
src/ftdi.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++-
src/ftdi.h | 20 +++++
5 files changed, 407 insertions(+), 1 deletions(-)
create mode 100644 examples/stream_test.c
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3e6c96d..b561418 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,6 +15,7 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE Debug)
endif("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_COLOR_MAKEFILE ON)
+set(CMAKE_CXX_FLAGS "-g -Wall")
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
# Debug build
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..33c26e2
--- /dev/null
+++ b/examples/stream_test.c
@@ -0,0 +1,168 @@
+/* stream_test.c
+ *
+ * Test reading from FT2232H in synchronous FIFO mode.
+ *
+ * The FT2232H must supply data due to an appropriate circuit
+ *
+ * After start, data will be read in streaming until the program is aborted
+ * Progess information wil be prointed 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 <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <errno.h>
+#include <ftdi.h>
+
+static FILE *outputFile;
+
+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 [options...] \n"
+ "Test streaming read from FT2232H\n"
+ "\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 int
+readCallback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void *userdata)
+{
+ if (length) {
+ 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 \n",
+ progress->totalTime,
+ progress->current.totalBytes / (1024.0 * 1024.0),
+ progress->currentRate / 1024.0,
+ progress->totalRate / 1024.0);
+ }
+ return exitRequested ? 1 : 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct ftdi_context ftdic;
+ int err, c;
+ char const *outfile = 0;
+ outputFile =0;
+
+ exitRequested = 0;
+
+ while (1) {
+ int option_index;
+ static struct option long_options[] = {
+ {NULL},
+ };
+
+ c = getopt_long(argc, argv, "", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if (optind == argc - 1) {
+ // Exactly one extra argument- a trace 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, NULL, NULL) < 0)
+ {
+ fprintf(stderr,"Can't open ftdi device: %s\n",ftdi_get_error_string(&ftdic));
+ return EXIT_FAILURE;
+ }
+
+ if(ftdi_usb_purge_buffers(&ftdic) < 0)
+ {
+ fprintf(stderr,"Can't purge\n",ftdi_get_error_string(&ftdic));
+ return EXIT_FAILURE;
+ }
+
+ usleep(10000);
+ if (ftdi_set_bitmode(&ftdic, 0xff, BITMODE_SYNCFF) < 0)
+ {
+ fprintf(stderr,"Can't set synchronous fifo mode\n",ftdi_get_error_string(&ftdic));
+ return EXIT_FAILURE;
+ }
+
+ if (outfile)
+ if ((outputFile = fopen(outfile,"w+")) == 0)
+ fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile, strerror(errno));
+
+ 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);
+ exit (0);
+}
+
+
+
diff --git a/src/ftdi.c b/src/ftdi.c
index 5c25abd..8c6dab6 100644
--- a/src/ftdi.c
+++ b/src/ftdi.c
@@ -236,7 +236,7 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli
int count = 0;
int i = 0;
- if (libusb_init(NULL) < 0)
+ if (libusb_init(&ftdi->libusb) < 0)
ftdi_error_return(-4, "libusb_init() failed");
if (libusb_get_device_list(NULL, &devs) < 0)
@@ -477,6 +477,11 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
// set configuration (needed especially for windows)
// tolerate EBUSY: one device with one configuration, but two interfaces
// and libftdi sessions to both interfaces (e.g. FT2232)
+ if (libusb_set_configuration(ftdi->usb_dev, cfg0) < 0)
+ {
+ ftdi_usb_close_internal (ftdi);
+ ftdi_error_return(-3, "unable to set usb configuration. Make sure ftdi_sio is unloaded!");
+ }
if (desc.bNumConfigurations > 0 && cfg != cfg0)
{
if (libusb_set_configuration(ftdi->usb_dev, cfg0) < 0)
@@ -1736,6 +1741,216 @@ int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunks
return 0;
}
+typedef struct {
+ FTDIStreamCallback *callback;
+ void *userdata;
+ int packetsize;
+ int result;
+ FTDIProgressInfo progress;
+} FTDIStreamState;
+
+static void
+ftdi_readstream_cb(struct libusb_transfer *transfer)
+{
+ FTDIStreamState *state = transfer->user_data;
+ int packet_size = state->packetsize;
+
+ if (state->result == 0) {
+ if (transfer->status == LIBUSB_TRANSFER_COMPLETED
+ || transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+
+ int i;
+ uint8_t *ptr = transfer->buffer;
+ int length = transfer->actual_length;
+ int numPackets = (length + packet_size - 1) / packet_size;
+
+ 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;
+
+ state->result = state->callback(ptr + 2, payloadLen,
+ NULL, state->userdata);
+ if (state->result)
+ break;
+
+ ptr += packetLen;
+ length -= packetLen;
+ }
+ if(transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+ free(transfer->buffer);
+ libusb_free_transfer(transfer);
+ return;
+ }
+
+
+ } else {
+ state->result = LIBUSB_ERROR_IO;
+ }
+ }
+
+ if (state->result == 0) {
+ transfer->status = -1;
+ state->result = libusb_submit_transfer(transfer);
+ }
+}
+
+/**
+ 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 };
+ 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;
+ }
+
+ /*
+ * 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(ftdi->libusb, &timeout);
+ if (!state.result) {
+ state.result = err;
+ }
+
+ // 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.result = 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) {
+ int i;
+ for (xferIndex = 0; xferIndex < numTransfers; xferIndex++) {
+ struct libusb_transfer *transfer = transfers[xferIndex];
+
+ if (transfer) {
+ if (transfer->status == -1)
+ libusb_cancel_transfer(transfer);
+ }
+ }
+ for(i=0; i<numTransfers; i++) {
+ libusb_handle_events(ftdi->libusb);
+ }
+ free(transfers);
+ }
+
+ if (err)
+ return err;
+ else
+ return state.result;
+}
+
/**
Enable bitbang mode.
diff --git a/src/ftdi.h b/src/ftdi.h
index 179d3ca..a148133 100644
--- a/src/ftdi.h
+++ b/src/ftdi.h
@@ -175,6 +175,8 @@ struct ftdi_transfer_control
struct ftdi_context
{
/* USB specific */
+ /** libusb's context */
+ struct libusb_context *libusb;
/** libusb's usb_dev_handle */
struct libusb_device_handle *usb_dev;
/** usb read timeout */
@@ -278,6 +280,24 @@ struct ftdi_eeprom
int size;
};
+/**
+ \brief Progress Info for streaming read
+*/
+typedef struct {
+ struct {
+ uint64_t totalBytes;
+ struct timeval time;
+ } first, prev, current;
+
+ double totalTime;
+ double totalRate;
+ double currentRate;
+} FTDIProgressInfo;
+
+typedef int (FTDIStreamCallback)(uint8_t *buffer, int length,
+ FTDIProgressInfo *progress, void *userdata);
+
+
#ifdef __cplusplus
extern "C"
{
--
1.6.4.2
--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to libftdi+unsubscribe@xxxxxxxxxxxxxxxxxxxxxxx
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to libftdi+unsubscribe@xxxxxxxxxxxxxxxxxxxxxxx
|