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 < [email protected]> wrote: > Hello, > > appended patch adds a streaming read for libftdi-1. It's mostly taken from > Micah Dowty <[email protected]> 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 [email protected] > > 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 <[email protected]> > Date: Tue, 27 Apr 2010 19:06:59 +0200 > Subject: Add streaming read from Micah Dowty <[email protected]> > 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 <[email protected]>\n", > + "Adapted for use with libftdi (C) 2010 Uwe Bonnes < > [email protected]>\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 > [email protected]<libftdi%[email protected]> > > -- libftdi - see http://www.intra2net.com/en/developer/libftdi for details. To unsubscribe send a mail to [email protected]
