libftdi1  1.0
ftdi_stream.c
Go to the documentation of this file.
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 {
51  void *userdata;
53  int activity;
54  int result;
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 
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 
146 int
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