Fix ftdi_stream.c compilation on WIN32
[libftdi] / src / ftdi_stream.c
CommitLineData
40da63b1
UB
1/***************************************************************************
2 ftdi_stream.c - description
3 -------------------
4 copyright : (C) 2009 Micah Dowty 2010 Uwe Bonnes
5 email : opensource@intra2net.com
6 ***************************************************************************/
74387f27 7
40da63b1
UB
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
74387f27 16/* Adapted from
40da63b1
UB
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>
41fdb68d 44#ifndef _WIN32
5a37dcb7 45#include <sys/time.h>
41fdb68d 46#endif
fec55667 47#include <libusb.h>
40da63b1
UB
48
49#include "ftdi.h"
50
51typedef struct
52{
53 FTDIStreamCallback *callback;
54 void *userdata;
55 int packetsize;
705f012d 56 int activity;
40da63b1
UB
57 int result;
58 FTDIProgressInfo progress;
59} FTDIStreamState;
60
705f012d 61/* Handle callbacks
74387f27 62 *
705f012d
UB
63 * With Exit request, free memory and release the transfer
64 *
74387f27 65 * state->result is only set when some error happens
705f012d 66 */
32e2d8b0 67static void LIBUSB_CALL
40da63b1
UB
68ftdi_readstream_cb(struct libusb_transfer *transfer)
69{
74387f27
TJ
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 }
40da63b1
UB
115}
116
117/**
118 Helper function to calculate (unix) time differences
119
120 \param a timeval
121 \param b timeval
122*/
123static double
124TimevalDiff(const struct timeval *a, const struct timeval *b)
125{
74387f27 126 return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec);
40da63b1
UB
127}
128
129/**
130 Streaming reading of data from the device
74387f27 131
40da63b1
UB
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
149int
150ftdi_readstream(struct ftdi_context *ftdi,
74387f27
TJ
151 FTDIStreamCallback *callback, void *userdata,
152 int packetsPerTransfer, int numTransfers)
40da63b1
UB
153{
154 struct libusb_transfer **transfers;
705f012d 155 FTDIStreamState state = { callback, userdata, ftdi->max_packet_size, 1 };
40da63b1
UB
156 int bufferSize = packetsPerTransfer * ftdi->max_packet_size;
157 int xferIndex;
158 int err = 0;
71ac8745
UB
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 }
74387f27 166
a3886df3
UB
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 }
74387f27 173
a3886df3
UB
174 /* Purge anything remaining in the buffers*/
175 if (ftdi_usb_purge_buffers(ftdi) < 0)
176 {
177 fprintf(stderr,"Can't Purge\n");
178 return 1;
179 }
180
40da63b1
UB
181 /*
182 * Set up all transfers
183 */
74387f27 184
40da63b1 185 transfers = calloc(numTransfers, sizeof *transfers);
74387f27
TJ
186 if (!transfers)
187 {
40da63b1
UB
188 err = LIBUSB_ERROR_NO_MEM;
189 goto cleanup;
190 }
74387f27 191
40da63b1
UB
192 for (xferIndex = 0; xferIndex < numTransfers; xferIndex++)
193 {
194 struct libusb_transfer *transfer;
74387f27 195
40da63b1
UB
196 transfer = libusb_alloc_transfer(0);
197 transfers[xferIndex] = transfer;
74387f27
TJ
198 if (!transfer)
199 {
40da63b1
UB
200 err = LIBUSB_ERROR_NO_MEM;
201 goto cleanup;
202 }
74387f27 203
40da63b1 204 libusb_fill_bulk_transfer(transfer, ftdi->usb_dev, ftdi->out_ep,
74387f27
TJ
205 malloc(bufferSize), bufferSize,
206 ftdi_readstream_cb,
40da63b1 207 &state, 0);
74387f27
TJ
208
209 if (!transfer->buffer)
210 {
40da63b1
UB
211 err = LIBUSB_ERROR_NO_MEM;
212 goto cleanup;
213 }
74387f27 214
40da63b1
UB
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.
74387f27
TJ
222 * Otherwise the transfers start stuttering and the PC not
223 * fetching data for several to several ten milliseconds
40da63b1
UB
224 * and we skip blocks
225 */
226 if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_SYNCFF) < 0)
227 {
97c6b5f6 228 fprintf(stderr,"Can't set synchronous fifo mode: %s\n",
40da63b1
UB
229 ftdi_get_error_string(ftdi));
230 goto cleanup;
231 }
232
233 /*
234 * Run the transfers, and periodically assess progress.
235 */
74387f27 236
40da63b1 237 gettimeofday(&state.progress.first.time, NULL);
74387f27 238
40da63b1
UB
239 do
240 {
241 FTDIProgressInfo *progress = &state.progress;
242 const double progressInterval = 1.0;
f838a4e3 243 struct timeval timeout = { 0, ftdi->usb_read_timeout * 1000};
40da63b1 244 struct timeval now;
74387f27 245
02212d8e 246 int err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
705f012d
UB
247 if (err == LIBUSB_ERROR_INTERRUPTED)
248 /* restart interrupted events */
74387f27 249 err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
40da63b1
UB
250 if (!state.result)
251 {
252 state.result = err;
253 }
705f012d
UB
254 if (state.activity == 0)
255 state.result = 1;
256 else
257 state.activity = 0;
74387f27 258
40da63b1
UB
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);
74387f27 266
40da63b1
UB
267 if (progress->prev.totalBytes)
268 {
269 // We have enough information to calculate rates
74387f27 270
40da63b1 271 double currentTime;
74387f27 272
40da63b1
UB
273 currentTime = TimevalDiff(&progress->current.time,
274 &progress->prev.time);
74387f27
TJ
275
276 progress->totalRate =
277 progress->current.totalBytes /progress->totalTime;
278 progress->currentRate =
279 (progress->current.totalBytes -
280 progress->prev.totalBytes) / currentTime;
40da63b1 281 }
74387f27 282
705f012d 283 state.callback(NULL, 0, progress, state.userdata);
40da63b1 284 progress->prev = progress->current;
74387f27 285
40da63b1
UB
286 }
287 } while (!state.result);
74387f27 288
40da63b1
UB
289 /*
290 * Cancel any outstanding transfers, and free memory.
291 */
74387f27
TJ
292
293cleanup:
40da63b1 294 fprintf(stderr, "cleanup\n");
705f012d 295 if (transfers)
74387f27 296 free(transfers);
40da63b1
UB
297 if (err)
298 return err;
299 else
300 return state.result;
301}
302