Implement tc[io]flush methods & deprecate broken purge_buffers methods.
[libftdi] / src / ftdi_stream.c
1 /***************************************************************************
2                           ftdi_stream.c  -  description
3                              -------------------
4     copyright            : (C) 2009 Micah Dowty 2010 Uwe Bonnes
5     email                : opensource@intra2net.com
6  ***************************************************************************/
7
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU Lesser General Public License           *
12  *   version 2.1 as published by the Free Software Foundation;             *
13  *                                                                         *
14  ***************************************************************************/
15
16 /* Adapted from
17  * fastftdi.c - A minimal FTDI FT232H interface for which supports bit-bang
18  *              mode, but focuses on very high-performance support for
19  *              synchronous FIFO mode. Requires libusb-1.0
20  *
21  * Copyright (C) 2009 Micah Dowty
22  *
23  * Permission is hereby granted, free of charge, to any person obtaining a copy
24  * of this software and associated documentation files (the "Software"), to deal
25  * in the Software without restriction, including without limitation the rights
26  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27  * copies of the Software, and to permit persons to whom the Software is
28  * furnished to do so, subject to the following conditions:
29  *
30  * The above copyright notice and this permission notice shall be included in
31  * all copies or substantial portions of the Software.
32  *
33  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39  * THE SOFTWARE.
40  */
41
42 #include <stdlib.h>
43 #include <stdio.h>
44 #ifndef _WIN32
45 #include <sys/time.h>
46 #endif
47 #include <libusb.h>
48
49 #include "ftdi.h"
50
51 typedef struct
52 {
53     FTDIStreamCallback *callback;
54     void *userdata;
55     int packetsize;
56     int activity;
57     int result;
58     FTDIProgressInfo progress;
59 } FTDIStreamState;
60
61 /* Handle callbacks
62  *
63  * With Exit request, free memory and release the transfer
64  *
65  * state->result is only set when some error happens
66  */
67 static void LIBUSB_CALL
68 ftdi_readstream_cb(struct libusb_transfer *transfer)
69 {
70     FTDIStreamState *state = transfer->user_data;
71     int packet_size = state->packetsize;
72
73     state->activity++;
74     if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
75     {
76         int i;
77         uint8_t *ptr = transfer->buffer;
78         int length = transfer->actual_length;
79         int numPackets = (length + packet_size - 1) / packet_size;
80         int res = 0;
81
82         for (i = 0; i < numPackets; i++)
83         {
84             int payloadLen;
85             int packetLen = length;
86
87             if (packetLen > packet_size)
88                 packetLen = packet_size;
89
90             payloadLen = packetLen - 2;
91             state->progress.current.totalBytes += payloadLen;
92
93             res = state->callback(ptr + 2, payloadLen,
94                                   NULL, state->userdata);
95
96             ptr += packetLen;
97             length -= packetLen;
98         }
99         if (res)
100         {
101             free(transfer->buffer);
102             libusb_free_transfer(transfer);
103         }
104         else
105         {
106             transfer->status = -1;
107             state->result = libusb_submit_transfer(transfer);
108         }
109     }
110     else
111     {
112         fprintf(stderr, "unknown status %d\n",transfer->status);
113         state->result = LIBUSB_ERROR_IO;
114     }
115 }
116
117 /**
118    Helper function to calculate (unix) time differences
119
120    \param a timeval
121    \param b timeval
122 */
123 static double
124 TimevalDiff(const struct timeval *a, const struct timeval *b)
125 {
126     return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec);
127 }
128
129 /**
130     Streaming reading of data from the device
131
132     Use asynchronous transfers in libusb-1.0 for high-performance
133     streaming of data from a device interface back to the PC. This
134     function continuously transfers data until either an error occurs
135     or the callback returns a nonzero value. This function returns
136     a libusb error code or the callback's return value.
137
138     For every contiguous block of received data, the callback will
139     be invoked.
140
141     \param  ftdi pointer to ftdi_context
142     \param  callback to user supplied function for one block of data
143     \param  userdata
144     \param  packetsPerTransfer number of packets per transfer
145     \param  numTransfers Number of transfers per callback
146
147 */
148
149 int
150 ftdi_readstream(struct ftdi_context *ftdi,
151                 FTDIStreamCallback *callback, void *userdata,
152                 int packetsPerTransfer, int numTransfers)
153 {
154     struct libusb_transfer **transfers;
155     FTDIStreamState state = { callback, userdata, ftdi->max_packet_size, 1 };
156     int bufferSize = packetsPerTransfer * ftdi->max_packet_size;
157     int xferIndex;
158     int err = 0;
159
160     /* Only FT2232H and FT232H know about the synchronous FIFO Mode*/
161     if ((ftdi->type != TYPE_2232H) && (ftdi->type != TYPE_232H))
162     {
163         fprintf(stderr,"Device doesn't support synchronous FIFO mode\n");
164         return 1;
165     }
166
167     /* We don't know in what state we are, switch to reset*/
168     if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_RESET) < 0)
169     {
170         fprintf(stderr,"Can't reset mode\n");
171         return 1;
172     }
173
174     /* Purge anything remaining in the buffers*/
175     if (ftdi_tcioflush(ftdi) < 0)
176     {
177         fprintf(stderr,"Can't flush FIFOs & buffers\n");
178         return 1;
179     }
180
181     /*
182      * Set up all transfers
183      */
184
185     transfers = calloc(numTransfers, sizeof *transfers);
186     if (!transfers)
187     {
188         err = LIBUSB_ERROR_NO_MEM;
189         goto cleanup;
190     }
191
192     for (xferIndex = 0; xferIndex < numTransfers; xferIndex++)
193     {
194         struct libusb_transfer *transfer;
195
196         transfer = libusb_alloc_transfer(0);
197         transfers[xferIndex] = transfer;
198         if (!transfer)
199         {
200             err = LIBUSB_ERROR_NO_MEM;
201             goto cleanup;
202         }
203
204         libusb_fill_bulk_transfer(transfer, ftdi->usb_dev, ftdi->out_ep,
205                                   malloc(bufferSize), bufferSize,
206                                   ftdi_readstream_cb,
207                                   &state, 0);
208
209         if (!transfer->buffer)
210         {
211             err = LIBUSB_ERROR_NO_MEM;
212             goto cleanup;
213         }
214
215         transfer->status = -1;
216         err = libusb_submit_transfer(transfer);
217         if (err)
218             goto cleanup;
219     }
220
221     /* Start the transfers only when everything has been set up.
222      * Otherwise the transfers start stuttering and the PC not
223      * fetching data for several to several ten milliseconds
224      * and we skip blocks
225      */
226     if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_SYNCFF) < 0)
227     {
228         fprintf(stderr,"Can't set synchronous fifo mode: %s\n",
229                 ftdi_get_error_string(ftdi));
230         goto cleanup;
231     }
232
233     /*
234      * Run the transfers, and periodically assess progress.
235      */
236
237     gettimeofday(&state.progress.first.time, NULL);
238
239     do
240     {
241         FTDIProgressInfo  *progress = &state.progress;
242         const double progressInterval = 1.0;
243         struct timeval timeout = { 0, ftdi->usb_read_timeout * 1000};
244         struct timeval now;
245
246         int err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
247         if (err ==  LIBUSB_ERROR_INTERRUPTED)
248             /* restart interrupted events */
249             err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
250         if (!state.result)
251         {
252             state.result = err;
253         }
254         if (state.activity == 0)
255             state.result = 1;
256         else
257             state.activity = 0;
258
259         // If enough time has elapsed, update the progress
260         gettimeofday(&now, NULL);
261         if (TimevalDiff(&now, &progress->current.time) >= progressInterval)
262         {
263             progress->current.time = now;
264             progress->totalTime = TimevalDiff(&progress->current.time,
265                                               &progress->first.time);
266
267             if (progress->prev.totalBytes)
268             {
269                 // We have enough information to calculate rates
270
271                 double currentTime;
272
273                 currentTime = TimevalDiff(&progress->current.time,
274                                           &progress->prev.time);
275
276                 progress->totalRate =
277                     progress->current.totalBytes /progress->totalTime;
278                 progress->currentRate =
279                     (progress->current.totalBytes -
280                      progress->prev.totalBytes) / currentTime;
281             }
282
283             state.callback(NULL, 0, progress, state.userdata);
284             progress->prev = progress->current;
285
286         }
287     } while (!state.result);
288
289     /*
290      * Cancel any outstanding transfers, and free memory.
291      */
292
293 cleanup:
294     fprintf(stderr, "cleanup\n");
295     if (transfers)
296         free(transfers);
297     if (err)
298         return err;
299     else
300         return state.result;
301 }
302