ftdi_stream: fix timeout setting: tv_usec field of timeval is in microseconds, not ms
[libftdi] / src / ftdi_stream.c
index 3f12ebc..2ad54c0 100644 (file)
@@ -4,7 +4,7 @@
     copyright            : (C) 2009 Micah Dowty 2010 Uwe Bonnes
     email                : opensource@intra2net.com
  ***************************************************************************/
+
 /***************************************************************************
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -13,7 +13,7 @@
  *                                                                         *
  ***************************************************************************/
 
-/* Adapted from 
+/* 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
@@ -41,6 +41,7 @@
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <libusb.h>
 
 #include "ftdi.h"
 
@@ -49,67 +50,65 @@ typedef struct
     FTDIStreamCallback *callback;
     void *userdata;
     int packetsize;
+    int activity;
     int result;
     FTDIProgressInfo progress;
 } FTDIStreamState;
 
-static void
+/* Handle callbacks
+ *
+ * With Exit request, free memory and release the transfer
+ *
+ * state->result is only set when some error happens
+ */
+static void LIBUSB_CALL
 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);
-   }
+    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 = 0;
+
+        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;
+    }
 }
 
 /**
@@ -121,7 +120,7 @@ ftdi_readstream_cb(struct libusb_transfer *transfer)
 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);
+    return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec);
 }
 
 /**
@@ -146,22 +145,43 @@ TimevalDiff(const struct timeval *a, const struct timeval *b)
 
 int
 ftdi_readstream(struct ftdi_context *ftdi,
-                      FTDIStreamCallback *callback, void *userdata,
-                      int packetsPerTransfer, int numTransfers)
+                FTDIStreamCallback *callback, void *userdata,
+                int packetsPerTransfer, int numTransfers)
 {
     struct libusb_transfer **transfers;
-    FTDIStreamState state = { callback, userdata, ftdi->max_packet_size };
+    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");
+    /* Only FT2232H and FT232H know about the synchronous FIFO Mode*/
+    if ((ftdi->type != TYPE_2232H) && (ftdi->type != TYPE_232H))
+    {
+        fprintf(stderr,"Device doesn't support synchronous FIFO mode\n");
+        return 1;
+    }
+
+    /* We don't know in what state we are, switch to reset*/
+    if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_RESET) < 0)
+    {
+        fprintf(stderr,"Can't reset mode\n");
+        return 1;
+    }
+
+    /* Purge anything remaining in the buffers*/
+    if (ftdi_usb_purge_buffers(ftdi) < 0)
+    {
+        fprintf(stderr,"Can't Purge\n");
+        return 1;
+    }
+
     /*
      * Set up all transfers
      */
 
     transfers = calloc(numTransfers, sizeof *transfers);
-    if (!transfers) {
+    if (!transfers)
+    {
         err = LIBUSB_ERROR_NO_MEM;
         goto cleanup;
     }
@@ -172,17 +192,19 @@ ftdi_readstream(struct ftdi_context *ftdi,
 
         transfer = libusb_alloc_transfer(0);
         transfers[xferIndex] = transfer;
-        if (!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,
+                                  malloc(bufferSize), bufferSize,
+                                  ftdi_readstream_cb,
                                   &state, 0);
 
-        if (!transfer->buffer) {
+        if (!transfer->buffer)
+        {
             err = LIBUSB_ERROR_NO_MEM;
             goto cleanup;
         }
@@ -194,13 +216,13 @@ ftdi_readstream(struct ftdi_context *ftdi,
     }
 
     /* 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 
+     * 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",
+        fprintf(stderr,"Can't set synchronous fifo mode: %s\n",
                 ftdi_get_error_string(ftdi));
         goto cleanup;
     }
@@ -215,14 +237,21 @@ ftdi_readstream(struct ftdi_context *ftdi,
     {
         FTDIProgressInfo  *progress = &state.progress;
         const double progressInterval = 1.0;
-        struct timeval timeout = { 0, ftdi->usb_read_timeout };
+        struct timeval timeout = { 0, ftdi->usb_read_timeout * 1000};
         struct timeval now;
 
-        int err = libusb_handle_events_timeout(NULL, &timeout);
+        int err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
+        if (err ==  LIBUSB_ERROR_INTERRUPTED)
+            /* restart interrupted events */
+            err = libusb_handle_events_timeout(ftdi->usb_ctx, &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);
@@ -241,14 +270,14 @@ ftdi_readstream(struct ftdi_context *ftdi,
                 currentTime = TimevalDiff(&progress->current.time,
                                           &progress->prev.time);
 
-                progress->totalRate = 
-                   progress->current.totalBytes /progress->totalTime;
-                progress->currentRate = 
-                   (progress->current.totalBytes -
-                    progress->prev.totalBytes) / currentTime;
+                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);
+            state.callback(NULL, 0, progress, state.userdata);
             progress->prev = progress->current;
 
         }
@@ -258,22 +287,10 @@ ftdi_readstream(struct ftdi_context *ftdi,
      * Cancel any outstanding transfers, and free memory.
      */
 
- cleanup:
+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);
+    if (transfers)
         free(transfers);
-    }
-
     if (err)
         return err;
     else