libftdi1  1.0
ftdi_stream.c
Go to the documentation of this file.
00001 /***************************************************************************
00002                           ftdi_stream.c  -  description
00003                              -------------------
00004     copyright            : (C) 2009 Micah Dowty 2010 Uwe Bonnes
00005     email                : opensource@intra2net.com
00006  ***************************************************************************/
00007  
00008 /***************************************************************************
00009  *                                                                         *
00010  *   This program is free software; you can redistribute it and/or modify  *
00011  *   it under the terms of the GNU Lesser General Public License           *
00012  *   version 2.1 as published by the Free Software Foundation;             *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 /* Adapted from 
00017  * fastftdi.c - A minimal FTDI FT232H interface for which supports bit-bang
00018  *              mode, but focuses on very high-performance support for
00019  *              synchronous FIFO mode. Requires libusb-1.0
00020  *
00021  * Copyright (C) 2009 Micah Dowty
00022  *
00023  * Permission is hereby granted, free of charge, to any person obtaining a copy
00024  * of this software and associated documentation files (the "Software"), to deal
00025  * in the Software without restriction, including without limitation the rights
00026  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00027  * copies of the Software, and to permit persons to whom the Software is
00028  * furnished to do so, subject to the following conditions:
00029  *
00030  * The above copyright notice and this permission notice shall be included in
00031  * all copies or substantial portions of the Software.
00032  *
00033  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00034  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00035  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00036  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00037  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00038  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00039  * THE SOFTWARE.
00040  */
00041 
00042 #include <stdlib.h>
00043 #include <stdio.h>
00044 #include <libusb.h>
00045 
00046 #include "ftdi.h"
00047 
00048 typedef struct
00049 {
00050     FTDIStreamCallback *callback;
00051     void *userdata;
00052     int packetsize;
00053     int activity;
00054     int result;
00055     FTDIProgressInfo progress;
00056 } FTDIStreamState;
00057 
00058 /* Handle callbacks
00059  * 
00060  * With Exit request, free memory and release the transfer
00061  *
00062  * state->result is only set when some error happens 
00063  */
00064 static void
00065 ftdi_readstream_cb(struct libusb_transfer *transfer)
00066 {
00067    FTDIStreamState *state = transfer->user_data;
00068    int packet_size = state->packetsize;
00069 
00070    state->activity++;
00071    if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
00072    {
00073        int i;
00074        uint8_t *ptr = transfer->buffer;
00075        int length = transfer->actual_length;
00076        int numPackets = (length + packet_size - 1) / packet_size;
00077        int res = 0;
00078 
00079        for (i = 0; i < numPackets; i++)
00080        {
00081            int payloadLen;
00082            int packetLen = length;
00083            
00084            if (packetLen > packet_size)
00085                packetLen = packet_size;
00086            
00087            payloadLen = packetLen - 2;
00088            state->progress.current.totalBytes += payloadLen;
00089            
00090            res = state->callback(ptr + 2, payloadLen,
00091                                  NULL, state->userdata);
00092 
00093            ptr += packetLen;
00094            length -= packetLen;
00095        }
00096        if (res)
00097        {
00098            free(transfer->buffer);
00099            libusb_free_transfer(transfer);           
00100        }
00101        else
00102        {
00103            transfer->status = -1;
00104            state->result = libusb_submit_transfer(transfer);
00105        }
00106    }
00107    else
00108    {
00109        fprintf(stderr, "unknown status %d\n",transfer->status); 
00110        state->result = LIBUSB_ERROR_IO;
00111    }
00112 }
00113 
00120 static double
00121 TimevalDiff(const struct timeval *a, const struct timeval *b)
00122 {
00123    return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec);
00124 }
00125 
00146 int
00147 ftdi_readstream(struct ftdi_context *ftdi,
00148                       FTDIStreamCallback *callback, void *userdata,
00149                       int packetsPerTransfer, int numTransfers)
00150 {
00151     struct libusb_transfer **transfers;
00152     FTDIStreamState state = { callback, userdata, ftdi->max_packet_size, 1 };
00153     int bufferSize = packetsPerTransfer * ftdi->max_packet_size;
00154     int xferIndex;
00155     int err = 0;
00156 
00157     /* Only FT2232H and FT232H know about the synchronous FIFO Mode*/
00158     if ((ftdi->type != TYPE_2232H) && (ftdi->type != TYPE_232H))
00159     {
00160         fprintf(stderr,"Device doesn't support synchronous FIFO mode\n");
00161         return 1;
00162     }
00163     
00164     /* We don't know in what state we are, switch to reset*/
00165     if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_RESET) < 0)
00166     {
00167         fprintf(stderr,"Can't reset mode\n");
00168         return 1;
00169     }
00170     
00171     /* Purge anything remaining in the buffers*/
00172     if (ftdi_usb_purge_buffers(ftdi) < 0)
00173     {
00174         fprintf(stderr,"Can't Purge\n");
00175         return 1;
00176     }
00177 
00178     /*
00179      * Set up all transfers
00180      */
00181     
00182     transfers = calloc(numTransfers, sizeof *transfers);
00183     if (!transfers) {
00184         err = LIBUSB_ERROR_NO_MEM;
00185         goto cleanup;
00186     }
00187     
00188     for (xferIndex = 0; xferIndex < numTransfers; xferIndex++)
00189     {
00190         struct libusb_transfer *transfer;
00191         
00192         transfer = libusb_alloc_transfer(0);
00193         transfers[xferIndex] = transfer;
00194         if (!transfer) {
00195             err = LIBUSB_ERROR_NO_MEM;
00196             goto cleanup;
00197         }
00198         
00199         libusb_fill_bulk_transfer(transfer, ftdi->usb_dev, ftdi->out_ep,
00200                                   malloc(bufferSize), bufferSize, 
00201                   ftdi_readstream_cb,
00202                                   &state, 0);
00203         
00204         if (!transfer->buffer) {
00205             err = LIBUSB_ERROR_NO_MEM;
00206             goto cleanup;
00207         }
00208         
00209         transfer->status = -1;
00210         err = libusb_submit_transfer(transfer);
00211         if (err)
00212             goto cleanup;
00213     }
00214 
00215     /* Start the transfers only when everything has been set up.
00216      * Otherwise the transfers start stuttering and the PC not 
00217      * fetching data for several to several ten milliseconds 
00218      * and we skip blocks
00219      */
00220     if (ftdi_set_bitmode(ftdi,  0xff, BITMODE_SYNCFF) < 0)
00221     {
00222         fprintf(stderr,"Can't set synchronous fifo mode: %s\n",
00223                 ftdi_get_error_string(ftdi));
00224         goto cleanup;
00225     }
00226 
00227     /*
00228      * Run the transfers, and periodically assess progress.
00229      */
00230     
00231     gettimeofday(&state.progress.first.time, NULL);
00232     
00233     do
00234     {
00235         FTDIProgressInfo  *progress = &state.progress;
00236         const double progressInterval = 1.0;
00237         struct timeval timeout = { 0, ftdi->usb_read_timeout };
00238         struct timeval now;
00239         
00240         int err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
00241         if (err ==  LIBUSB_ERROR_INTERRUPTED)
00242             /* restart interrupted events */
00243             err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);  
00244         if (!state.result)
00245         {
00246             state.result = err;
00247         }
00248         if (state.activity == 0)
00249             state.result = 1;
00250         else
00251             state.activity = 0;
00252         
00253         // If enough time has elapsed, update the progress
00254         gettimeofday(&now, NULL);
00255         if (TimevalDiff(&now, &progress->current.time) >= progressInterval)
00256         {
00257             progress->current.time = now;
00258             progress->totalTime = TimevalDiff(&progress->current.time,
00259                                               &progress->first.time);
00260             
00261             if (progress->prev.totalBytes)
00262             {
00263                 // We have enough information to calculate rates
00264                 
00265                 double currentTime;
00266                 
00267                 currentTime = TimevalDiff(&progress->current.time,
00268                                           &progress->prev.time);
00269                 
00270                 progress->totalRate = 
00271             progress->current.totalBytes /progress->totalTime;
00272                 progress->currentRate = 
00273             (progress->current.totalBytes -
00274              progress->prev.totalBytes) / currentTime;
00275             }
00276             
00277             state.callback(NULL, 0, progress, state.userdata);
00278             progress->prev = progress->current;
00279             
00280         }
00281     } while (!state.result);
00282     
00283     /*
00284      * Cancel any outstanding transfers, and free memory.
00285      */
00286     
00287  cleanup:
00288     fprintf(stderr, "cleanup\n");
00289     if (transfers)
00290        free(transfers);
00291     if (err)
00292         return err;
00293     else
00294         return state.result;
00295 }
00296