libftdi Archives

Subject: Please apply: Streaming read for libftdi-1

From: Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
To: libftdi@xxxxxxxxxxxxxxxxxxxxxxx
Date: Mon, 31 May 2010 16:01:18 +0200
Hello,

with appended code and a revised verilog code, I can reliable read blocks of
16 Bytes of date generated about each Microsecond, like running for about
500 seconds:

8 errors of 375293955 blocks (2.131662e-08), 9 (2.398120e-08) blocks skipped

>From time to time some one or two blocks are skipped, which is something to
expect, as I only have about 128 byte of additional FIFO beside the FT2232H
internal fifo. First test showed that a larger FIFO lead to some more skips,
either by some flaw in my logic or some strange behaviour of the FT2232H
chip.

Writing the generated data to disk leads to skips for several to several ten
microseconds each several to several seconds. Probably the BLK (big kernel
lock) hits here.

The patch only adds some definition to ftdi.h and the additional file
ftdi_stream.c, with Micah Dowty okay to dual license his code. so I hope it
is okay to apply. Should the verilog code be inclided in libftdi too?

Bye
-- 
Uwe Bonnes                bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Institut fuer Kernphysik  Schlossgartenstrasse 9  64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------
From: Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
Date: Mon, 31 May 2010 15:46:55 +0200
Subject: Provide a streaming read, base on code from Micah Dowty

---
 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       |  282 +++++++++++++++++++++++++++++++++++++
 6 files changed, 660 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..3133d0a
--- /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 uint32_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 %10d \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 %10d \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 \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;
+   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..3f0b9cc
--- /dev/null
+++ b/src/ftdi_stream.c
@@ -0,0 +1,282 @@
+/***************************************************************************
+                          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 result;
+    FTDIProgressInfo progress;
+} FTDIStreamState;
+
+static void
+ftdi_readstream_cb(struct libusb_transfer *transfer)
+{
+   FTDIStreamState *state = transfer->user_data;
+   int packet_size = state->packetsize;
+
+   if(transfer->status & LIBUSB_TRANSFER_CANCELLED)
+   {
+       free(transfer->buffer);
+       libusb_free_transfer(transfer);
+       return;
+   }
+   if (state->result == 0)
+   {
+       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;
+           
+           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;
+           }
+           
+           
+       }
+       else
+       {
+           fprintf(stderr, "unknown status %d\n",transfer->status); 
+           state->result = LIBUSB_ERROR_IO;
+       }
+   }
+   else
+       fprintf(stderr,"state->result %d\n", state->result);
+   
+   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;
+    }
+
+    /* 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 (!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) {
+                    libusb_cancel_transfer(transfer);
+            }
+        }
+            libusb_handle_events(NULL);
+        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   

Current Thread