X-Git-Url: http://developer.intra2net.com/git/?p=libftdi;a=blobdiff_plain;f=src%2Fftdi_stream.c;fp=src%2Fftdi_stream.c;h=3f12ebc5462ca8672a2c01f6f9a20b490a2042d7;hp=0000000000000000000000000000000000000000;hb=40da63b1f8924d2c4d1e34d137dc7d12b5b1c643;hpb=9965050222905eecc664baf168f71a2b632cae2a diff --git a/src/ftdi_stream.c b/src/ftdi_stream.c new file mode 100644 index 0000000..3f12ebc --- /dev/null +++ b/src/ftdi_stream.c @@ -0,0 +1,282 @@ +/*************************************************************************** + ftdi_stream.c - description + ------------------- + copyright : (C) 2009 Micah Dowty 2010 Uwe Bonnes + email : opensource@intra2net.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License * + * version 2.1 as published by the Free Software Foundation; * + * * + ***************************************************************************/ + +/* 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 + * + * Copyright (C) 2009 Micah Dowty + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "ftdi.h" + +typedef struct +{ + FTDIStreamCallback *callback; + void *userdata; + int packetsize; + int result; + FTDIProgressInfo progress; +} FTDIStreamState; + +static void +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); + } +} + +/** + Helper function to calculate (unix) time differences + + \param a timeval + \param b timeval +*/ +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); +} + +/** + Streaming reading of data from the device + + Use asynchronous transfers in libusb-1.0 for high-performance + streaming of data from a device interface back to the PC. This + function continuously transfers data until either an error occurs + or the callback returns a nonzero value. This function returns + a libusb error code or the callback's return value. + + For every contiguous block of received data, the callback will + be invoked. + + \param ftdi pointer to ftdi_context + \param callback to user supplied function for one block of data + \param userdata + \param packetsPerTransfer number of packets per transfer + \param numTransfers Number of transfers per callback + +*/ + +int +ftdi_readstream(struct ftdi_context *ftdi, + FTDIStreamCallback *callback, void *userdata, + int packetsPerTransfer, int numTransfers) +{ + struct libusb_transfer **transfers; + FTDIStreamState state = { callback, userdata, ftdi->max_packet_size }; + int bufferSize = packetsPerTransfer * ftdi->max_packet_size; + int xferIndex; + int err = 0; + + fprintf(stderr, "ftdi_readstream\n"); + /* + * Set up all transfers + */ + + transfers = calloc(numTransfers, sizeof *transfers); + if (!transfers) { + err = LIBUSB_ERROR_NO_MEM; + goto cleanup; + } + + for (xferIndex = 0; xferIndex < numTransfers; xferIndex++) + { + struct libusb_transfer *transfer; + + transfer = libusb_alloc_transfer(0); + transfers[xferIndex] = 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, + &state, 0); + + if (!transfer->buffer) { + err = LIBUSB_ERROR_NO_MEM; + goto cleanup; + } + + transfer->status = -1; + err = libusb_submit_transfer(transfer); + if (err) + goto cleanup; + } + + /* 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 + * and we skip blocks + */ + if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_SYNCFF) < 0) + { + fprintf(stderr,"Can't set synchronous fifo mode\n", + ftdi_get_error_string(ftdi)); + goto cleanup; + } + + /* + * Run the transfers, and periodically assess progress. + */ + + gettimeofday(&state.progress.first.time, NULL); + + do + { + FTDIProgressInfo *progress = &state.progress; + const double progressInterval = 1.0; + struct timeval timeout = { 0, ftdi->usb_read_timeout }; + struct timeval now; + + int err = libusb_handle_events_timeout(NULL, &timeout); + if (!state.result) + { + state.result = err; + } + + // If enough time has elapsed, update the progress + gettimeofday(&now, NULL); + if (TimevalDiff(&now, &progress->current.time) >= progressInterval) + { + progress->current.time = now; + progress->totalTime = TimevalDiff(&progress->current.time, + &progress->first.time); + + if (progress->prev.totalBytes) + { + // We have enough information to calculate rates + + double currentTime; + + currentTime = TimevalDiff(&progress->current.time, + &progress->prev.time); + + 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); + progress->prev = progress->current; + + } + } while (!state.result); + + /* + * Cancel any outstanding transfers, and free memory. + */ + + 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); + free(transfers); + } + + if (err) + return err; + else + return state.result; +} +