blob: cf3acf57305124bab4ebcce74451ebf1ac1a4c89 [file] [log] [blame]
/***************************************************************************
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 <stdlib.h>
#include <stdio.h>
#include <libusb.h>
#include "ftdi.h"
typedef struct
{
FTDIStreamCallback *callback;
void *userdata;
int packetsize;
int activity;
int result;
FTDIProgressInfo progress;
} FTDIStreamState;
/* Handle callbacks
*
* With Exit request, free memory and release the transfer
*
* state->result is only set when some error happens
*/
static void
ftdi_readstream_cb(struct libusb_transfer *transfer)
{
FTDIStreamState *state = transfer->user_data;
int packet_size = state->packetsize;
state->activity++;
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;
int res = 0;
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;
res = state->callback(ptr + 2, payloadLen,
NULL, state->userdata);
ptr += packetLen;
length -= packetLen;
}
if (res)
{
free(transfer->buffer);
libusb_free_transfer(transfer);
}
else
{
transfer->status = -1;
state->result = libusb_submit_transfer(transfer);
}
}
else
{
fprintf(stderr, "unknown status %d\n",transfer->status);
state->result = LIBUSB_ERROR_IO;
}
}
/**
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, 1 };
int bufferSize = packetsPerTransfer * ftdi->max_packet_size;
int xferIndex;
int err = 0;
/* Only FT2232H and FT232H know about the synchronous FIFO Mode*/
if ((ftdi->type != TYPE_2232H) && (ftdi->type != TYPE_232H))
{
fprintf(stderr,"Device doesn't support synchronous FIFO mode\n");
return 1;
}
/* We don't know in what state we are, switch to reset*/
if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_RESET) < 0)
{
fprintf(stderr,"Can't reset mode\n");
return 1;
}
/* Purge anything remaining in the buffers*/
if (ftdi_usb_purge_buffers(ftdi) < 0)
{
fprintf(stderr,"Can't Purge\n");
return 1;
}
/*
* 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: %s\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(ftdi->usb_ctx, &timeout);
if (err == LIBUSB_ERROR_INTERRUPTED)
/* restart interrupted events */
err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
if (!state.result)
{
state.result = err;
}
if (state.activity == 0)
state.result = 1;
else
state.activity = 0;
// 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.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)
free(transfers);
if (err)
return err;
else
return state.result;
}