Don't #include <libusb.h> from ftdi.h
[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 #include <libusb.h>
45
46 #include "ftdi.h"
47
48 typedef struct
49 {
50     FTDIStreamCallback *callback;
51     void *userdata;
52     int packetsize;
53     int activity;
54     int result;
55     FTDIProgressInfo progress;
56 } FTDIStreamState;
57
58 /* Handle callbacks
59  * 
60  * With Exit request, free memory and release the transfer
61  *
62  * state->result is only set when some error happens 
63  */
64 static void
65 ftdi_readstream_cb(struct libusb_transfer *transfer)
66 {
67    FTDIStreamState *state = transfer->user_data;
68    int packet_size = state->packetsize;
69
70    state->activity++;
71    if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
72    {
73        int i;
74        uint8_t *ptr = transfer->buffer;
75        int length = transfer->actual_length;
76        int numPackets = (length + packet_size - 1) / packet_size;
77        int res = 0;
78
79        for (i = 0; i < numPackets; i++)
80        {
81            int payloadLen;
82            int packetLen = length;
83            
84            if (packetLen > packet_size)
85                packetLen = packet_size;
86            
87            payloadLen = packetLen - 2;
88            state->progress.current.totalBytes += payloadLen;
89            
90            res = state->callback(ptr + 2, payloadLen,
91                                  NULL, state->userdata);
92
93            ptr += packetLen;
94            length -= packetLen;
95        }
96        if (res)
97        {
98            free(transfer->buffer);
99            libusb_free_transfer(transfer);           
100        }
101        else
102        {
103            transfer->status = -1;
104            state->result = libusb_submit_transfer(transfer);
105        }
106    }
107    else
108    {
109        fprintf(stderr, "unknown status %d\n",transfer->status); 
110        state->result = LIBUSB_ERROR_IO;
111    }
112 }
113
114 /**
115    Helper function to calculate (unix) time differences
116
117    \param a timeval
118    \param b timeval
119 */
120 static double
121 TimevalDiff(const struct timeval *a, const struct timeval *b)
122 {
123    return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec);
124 }
125
126 /**
127     Streaming reading of data from the device
128     
129     Use asynchronous transfers in libusb-1.0 for high-performance
130     streaming of data from a device interface back to the PC. This
131     function continuously transfers data until either an error occurs
132     or the callback returns a nonzero value. This function returns
133     a libusb error code or the callback's return value.
134
135     For every contiguous block of received data, the callback will
136     be invoked.
137
138     \param  ftdi pointer to ftdi_context
139     \param  callback to user supplied function for one block of data
140     \param  userdata
141     \param  packetsPerTransfer number of packets per transfer
142     \param  numTransfers Number of transfers per callback
143
144 */
145
146 int
147 ftdi_readstream(struct ftdi_context *ftdi,
148                       FTDIStreamCallback *callback, void *userdata,
149                       int packetsPerTransfer, int numTransfers)
150 {
151     struct libusb_transfer **transfers;
152     FTDIStreamState state = { callback, userdata, ftdi->max_packet_size, 1 };
153     int bufferSize = packetsPerTransfer * ftdi->max_packet_size;
154     int xferIndex;
155     int err = 0;
156
157     /* Only FT2232H and FT232H know about the synchronous FIFO Mode*/
158     if ((ftdi->type != TYPE_2232H) && (ftdi->type != TYPE_232H))
159     {
160         fprintf(stderr,"Device doesn't support synchronous FIFO mode\n");
161         return 1;
162     }
163     
164     /* We don't know in what state we are, switch to reset*/
165     if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_RESET) < 0)
166     {
167         fprintf(stderr,"Can't reset mode\n");
168         return 1;
169     }
170     
171     /* Purge anything remaining in the buffers*/
172     if (ftdi_usb_purge_buffers(ftdi) < 0)
173     {
174         fprintf(stderr,"Can't Purge\n");
175         return 1;
176     }
177
178     /*
179      * Set up all transfers
180      */
181     
182     transfers = calloc(numTransfers, sizeof *transfers);
183     if (!transfers) {
184         err = LIBUSB_ERROR_NO_MEM;
185         goto cleanup;
186     }
187     
188     for (xferIndex = 0; xferIndex < numTransfers; xferIndex++)
189     {
190         struct libusb_transfer *transfer;
191         
192         transfer = libusb_alloc_transfer(0);
193         transfers[xferIndex] = transfer;
194         if (!transfer) {
195             err = LIBUSB_ERROR_NO_MEM;
196             goto cleanup;
197         }
198         
199         libusb_fill_bulk_transfer(transfer, ftdi->usb_dev, ftdi->out_ep,
200                                   malloc(bufferSize), bufferSize, 
201                                   ftdi_readstream_cb,
202                                   &state, 0);
203         
204         if (!transfer->buffer) {
205             err = LIBUSB_ERROR_NO_MEM;
206             goto cleanup;
207         }
208         
209         transfer->status = -1;
210         err = libusb_submit_transfer(transfer);
211         if (err)
212             goto cleanup;
213     }
214
215     /* Start the transfers only when everything has been set up.
216      * Otherwise the transfers start stuttering and the PC not 
217      * fetching data for several to several ten milliseconds 
218      * and we skip blocks
219      */
220     if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_SYNCFF) < 0)
221     {
222         fprintf(stderr,"Can't set synchronous fifo mode: %s\n",
223                 ftdi_get_error_string(ftdi));
224         goto cleanup;
225     }
226
227     /*
228      * Run the transfers, and periodically assess progress.
229      */
230     
231     gettimeofday(&state.progress.first.time, NULL);
232     
233     do
234     {
235         FTDIProgressInfo  *progress = &state.progress;
236         const double progressInterval = 1.0;
237         struct timeval timeout = { 0, ftdi->usb_read_timeout };
238         struct timeval now;
239         
240         int err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
241         if (err ==  LIBUSB_ERROR_INTERRUPTED)
242             /* restart interrupted events */
243             err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);  
244         if (!state.result)
245         {
246             state.result = err;
247         }
248         if (state.activity == 0)
249             state.result = 1;
250         else
251             state.activity = 0;
252         
253         // If enough time has elapsed, update the progress
254         gettimeofday(&now, NULL);
255         if (TimevalDiff(&now, &progress->current.time) >= progressInterval)
256         {
257             progress->current.time = now;
258             progress->totalTime = TimevalDiff(&progress->current.time,
259                                               &progress->first.time);
260             
261             if (progress->prev.totalBytes)
262             {
263                 // We have enough information to calculate rates
264                 
265                 double currentTime;
266                 
267                 currentTime = TimevalDiff(&progress->current.time,
268                                           &progress->prev.time);
269                 
270                 progress->totalRate = 
271                     progress->current.totalBytes /progress->totalTime;
272                 progress->currentRate = 
273                     (progress->current.totalBytes -
274                      progress->prev.totalBytes) / currentTime;
275             }
276             
277             state.callback(NULL, 0, progress, state.userdata);
278             progress->prev = progress->current;
279             
280         }
281     } while (!state.result);
282     
283     /*
284      * Cancel any outstanding transfers, and free memory.
285      */
286     
287  cleanup:
288     fprintf(stderr, "cleanup\n");
289     if (transfers)
290        free(transfers);
291     if (err)
292         return err;
293     else
294         return state.result;
295 }
296