ft2232 and usbblaster cable drivers ported to usbconn layer
git-svn-id: https://urjtag.svn.sourceforge.net/svnroot/urjtag/trunk@1244 b68d4a1b-bc3d-0410-92ed-d4ac073336b7master
parent
cf6e302089
commit
92c0c55e98
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by A. Laeuger, 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _USBCONN_LIBFTDX_H
|
||||
#define _USBCONN_LIBFTDX_H 1
|
||||
|
||||
#define FTDX_MAXSEND 4096
|
||||
|
||||
/* Maximum chunk to receive from ftdi/ftd2xx driver.
|
||||
Larger values might speed up comm, but there's an upper limit
|
||||
when too many bytes are sent and the underlying libftdi or libftd2xx
|
||||
don't fetch the returned data in time -> deadlock */
|
||||
#define FTDI_MAXRECV ( 4 * 64)
|
||||
#define FTD2XX_MAXRECV (63 * 64)
|
||||
#define FTDX_MAXRECV (FTD2XX_MAXRECV < FTDI_MAXRECV ? FTD2XX_MAXRECV : FTDI_MAXRECV)
|
||||
|
||||
#endif
|
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Generic command buffer handler.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by Arnim Laeuger, 2008.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sysdep.h"
|
||||
|
||||
#include "generic.h"
|
||||
#include "generic_usbconn.h"
|
||||
|
||||
#include "cmd_xfer.h"
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* extend_cmd_buffer( cmd )
|
||||
*
|
||||
* Extends the buffer of the given command if a new byte wouldn't fit into
|
||||
* the current buffer size.
|
||||
*
|
||||
* cmd : pointer to cx_cmd_t
|
||||
*
|
||||
* Return value:
|
||||
* 0 : Error occured, not enough memory
|
||||
* 1 : All ok
|
||||
*
|
||||
****************************************************************************/
|
||||
static int
|
||||
extend_cmd_buffer( cx_cmd_t *cmd )
|
||||
{
|
||||
/* check size of cmd buffer and increase it if not sufficient */
|
||||
if (cmd->buf_pos >= cmd->buf_len)
|
||||
{
|
||||
cmd->buf_len *= 2;
|
||||
if (cmd->buf)
|
||||
cmd->buf = (uint8_t *)realloc( cmd->buf, cmd->buf_len );
|
||||
}
|
||||
|
||||
return cmd->buf ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_cmd_push( cmd, d )
|
||||
*
|
||||
* Pushes the byte value d to the buffer of the current last command.
|
||||
*
|
||||
* cmd_root : pointer to cx_cmd_root_t struct
|
||||
* d : new value to be pushed
|
||||
*
|
||||
* Return value:
|
||||
* 0 : Error occured
|
||||
* 1 : All ok
|
||||
*
|
||||
****************************************************************************/
|
||||
int
|
||||
cx_cmd_push( cx_cmd_root_t *cmd_root, uint8_t d )
|
||||
{
|
||||
cx_cmd_t *cmd = cmd_root->last;
|
||||
|
||||
if (!cmd)
|
||||
return 0;
|
||||
|
||||
if (!extend_cmd_buffer( cmd ))
|
||||
return 0;
|
||||
|
||||
cmd->buf[cmd->buf_pos++] = d;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_cmd_dequeue( cmd_root )
|
||||
*
|
||||
* Dequeues the first command.
|
||||
*
|
||||
* cmd_root : pointer to cx_cmd_root_t parameter struct
|
||||
*
|
||||
* Return value:
|
||||
* NULL : Error occured
|
||||
* <>NULL : All ok, pointer to dequeued cx_cmd_t
|
||||
*
|
||||
****************************************************************************/
|
||||
cx_cmd_t *
|
||||
cx_cmd_dequeue( cx_cmd_root_t *cmd_root )
|
||||
{
|
||||
cx_cmd_t *cmd = cmd_root->first;
|
||||
|
||||
if (cmd)
|
||||
{
|
||||
if ((cmd_root->first = cmd->next) == NULL)
|
||||
cmd_root->last = NULL;
|
||||
cmd->next = NULL;
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_cmd_free( cmd )
|
||||
*
|
||||
* Frees allocated memory of specified cmd structure.
|
||||
*
|
||||
* cmd : pointer to cx_cmd_t
|
||||
*
|
||||
* Return value:
|
||||
* none
|
||||
*
|
||||
****************************************************************************/
|
||||
void
|
||||
cx_cmd_free( cx_cmd_t *cmd )
|
||||
{
|
||||
if (cmd)
|
||||
{
|
||||
if (cmd->buf)
|
||||
free( cmd->buf );
|
||||
free( cmd );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_cmd_queue( cmd_root, to_recv )
|
||||
*
|
||||
* Allocates a new cx_cmd_t and queues it at the end of the command
|
||||
* queue. The value of to_recv will be stored in the new cmd element,
|
||||
* set to 0 if this command will not generate receive bytes.
|
||||
*
|
||||
* cmd_root : pointer to cx_cmd_root_t parameter struct
|
||||
* to_recv : number of receive bytes that this command will generate
|
||||
*
|
||||
* Return value:
|
||||
* NULL : Error occured
|
||||
* <>NULL : All ok, pointer to allocated cx_cmd_t
|
||||
*
|
||||
****************************************************************************/
|
||||
cx_cmd_t *
|
||||
cx_cmd_queue( cx_cmd_root_t *cmd_root, uint32_t to_recv )
|
||||
{
|
||||
cx_cmd_t *cmd = (cx_cmd_t *)malloc( sizeof( cx_cmd_t ) );
|
||||
|
||||
if (cmd)
|
||||
{
|
||||
cmd->buf_len = 64;
|
||||
if ((cmd->buf = (uint8_t *)malloc( cmd->buf_len )) == NULL)
|
||||
{
|
||||
free( cmd );
|
||||
cmd = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->buf_pos = 0;
|
||||
cmd->to_recv = to_recv;
|
||||
cmd->next = NULL;
|
||||
if (!cmd_root->first)
|
||||
cmd_root->first = cmd;
|
||||
if (cmd_root->last)
|
||||
cmd_root->last->next = cmd;
|
||||
cmd_root->last = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_cmd_init( cmd_root )
|
||||
*
|
||||
* Initializes the command root structure.
|
||||
*
|
||||
* cmd_root : pointer to cx_cmd_root_t
|
||||
*
|
||||
* Return value:
|
||||
* none
|
||||
*
|
||||
****************************************************************************/
|
||||
void
|
||||
cx_cmd_init( cx_cmd_root_t *cmd_root )
|
||||
{
|
||||
cmd_root->first = NULL;
|
||||
cmd_root->last = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_cmd_deinit( cmd_root )
|
||||
*
|
||||
* Deinitialzes and frees all elements from the command root structure.
|
||||
*
|
||||
* cmd_root : pointer to cx_cmd_root_t
|
||||
*
|
||||
* Return value:
|
||||
* none
|
||||
*
|
||||
****************************************************************************/
|
||||
void
|
||||
cx_cmd_deinit( cx_cmd_root_t *cmd_root )
|
||||
{
|
||||
cx_cmd_t *cmd;
|
||||
while (cmd_root->first)
|
||||
{
|
||||
cmd = cx_cmd_dequeue( cmd_root );
|
||||
cx_cmd_free( cmd );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_xfer( cmd_root, out_cmd, cable, how_much )
|
||||
*
|
||||
* Unrolls the queued commands and posts their payload to the usbconn driver.
|
||||
* NB: usbconn_write will buffer the accumulated payload until usbconn_read
|
||||
* is called.
|
||||
*
|
||||
* Flushing of the posted payload bytes is triggered when how_much
|
||||
* requests to do so or if receive bytes are expected.
|
||||
*
|
||||
* cmd_root : pointer to cx_cmd_root_t struct
|
||||
* out_cmd : pointer to cx_cmd_t for an optional command that is appended
|
||||
* to send buffer in case commands have been scheduled that
|
||||
* yield return/receive data from the device
|
||||
* cable : current cable_t
|
||||
* how_much : cable_flush_amount_t value specifying the flush strategy
|
||||
*
|
||||
* Return value:
|
||||
* none
|
||||
*
|
||||
****************************************************************************/
|
||||
void
|
||||
cx_xfer( cx_cmd_root_t *cmd_root, const cx_cmd_t *out_cmd,
|
||||
cable_t *cable, cable_flush_amount_t how_much )
|
||||
{
|
||||
cx_cmd_t *cmd = cx_cmd_dequeue( cmd_root );
|
||||
uint32_t bytes_to_recv;
|
||||
|
||||
bytes_to_recv = 0;
|
||||
|
||||
while (cmd)
|
||||
{
|
||||
/* Step 1: copy command bytes buffered for sending them later
|
||||
through the usbconn driver */
|
||||
bytes_to_recv += cmd->to_recv;
|
||||
/* write command data (buffered) */
|
||||
usbconn_write( cable->link.usb, cmd->buf, cmd->buf_pos, cmd->to_recv );
|
||||
cx_cmd_free( cmd );
|
||||
cmd = cx_cmd_dequeue( cmd_root );
|
||||
}
|
||||
|
||||
/* it's possible for the caller to define an extra command that is
|
||||
appended right before sending commands to the device in case output
|
||||
data is expected */
|
||||
if (bytes_to_recv && out_cmd)
|
||||
{
|
||||
usbconn_write( cable->link.usb, out_cmd->buf, out_cmd->buf_pos, out_cmd->to_recv );
|
||||
bytes_to_recv += out_cmd->to_recv;
|
||||
}
|
||||
|
||||
if (bytes_to_recv || (how_much != TO_OUTPUT))
|
||||
{
|
||||
/* Step 2: flush scheduled bytes */
|
||||
usbconn_read( cable->link.usb, NULL, 0 );
|
||||
|
||||
bytes_to_recv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cx_xfer_recv( cable )
|
||||
*
|
||||
* Extracts the byte at the current position from the receive buffer.
|
||||
*
|
||||
* cable : pointer to the current cable struct
|
||||
*
|
||||
* Return value:
|
||||
* Byte value from receive buffer
|
||||
*
|
||||
****************************************************************************/
|
||||
uint8_t
|
||||
cx_xfer_recv( cable_t *cable )
|
||||
{
|
||||
uint8_t buf;
|
||||
|
||||
if (usbconn_read( cable->link.usb, &buf, 1 ) == 1)
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:C
|
||||
c-default-style:gnu
|
||||
indent-tabs-mode:nil
|
||||
End:
|
||||
*/
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Generic command buffer handler.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by Arnim Laeuger, 2008.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CMD_XFER_H
|
||||
#define CMD_XFER_H
|
||||
|
||||
#include "sysdep.h"
|
||||
|
||||
#include <cable.h>
|
||||
|
||||
/* description of a command
|
||||
the buffer can contain one or more commands if receive count
|
||||
is zero for all of them */
|
||||
typedef struct cx_cmd cx_cmd_t;
|
||||
struct cx_cmd {
|
||||
cx_cmd_t *next;
|
||||
uint32_t buf_len;
|
||||
uint32_t buf_pos;
|
||||
uint8_t *buf;
|
||||
uint32_t to_recv;
|
||||
};
|
||||
|
||||
struct cx_cmd_root {
|
||||
cx_cmd_t *first;
|
||||
cx_cmd_t *last;
|
||||
};
|
||||
typedef struct cx_cmd_root cx_cmd_root_t;
|
||||
|
||||
int cx_cmd_push( cx_cmd_root_t *cmd_root, uint8_t d);
|
||||
cx_cmd_t *cx_cmd_dequeue( cx_cmd_root_t *cmd_root );
|
||||
void cx_cmd_free( cx_cmd_t *cmd );
|
||||
cx_cmd_t *cx_cmd_queue( cx_cmd_root_t *cmd_root, uint32_t to_recv );
|
||||
void cx_cmd_init( cx_cmd_root_t *cmd_root );
|
||||
void cx_cmd_deinit( cx_cmd_root_t *cmd_root );
|
||||
|
||||
void cx_xfer( cx_cmd_root_t *cmd_root, const cx_cmd_t *out_cmd,
|
||||
cable_t *cable, cable_flush_amount_t how_much );
|
||||
uint8_t cx_xfer_recv( cable_t *cable );
|
||||
|
||||
#endif /* CMD_XFER_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,490 +0,0 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* libftd2xx Driver
|
||||
*
|
||||
* Based on libftdi driver
|
||||
* Copyright (C) 2006 K. Waschk
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by Kolja Waschk, 2006.
|
||||
* Structure taken from ppdev.c, written by Marcel Telka, 2003.
|
||||
* Ported to libftd2xx by A. Laeuger, 2007.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sysdep.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#if __CYGWIN__ || __MINGW32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#ifdef HAVE_STROPTS_H
|
||||
#include <stropts.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ftd2xx.h>
|
||||
|
||||
#include "parport.h"
|
||||
#include "cable.h"
|
||||
|
||||
parport_driver_t ftd2xx_parport_driver;
|
||||
parport_driver_t ftd2xx_mpsse_parport_driver;
|
||||
|
||||
typedef struct port_node_t port_node_t;
|
||||
|
||||
struct port_node_t {
|
||||
parport_t *port;
|
||||
port_node_t *next;
|
||||
};
|
||||
|
||||
static port_node_t *ports = NULL; /* devices */
|
||||
|
||||
#define OUTBUF_LEN_STD 64
|
||||
#define OUTBUF_LEN_MPSSE 4096
|
||||
|
||||
typedef struct {
|
||||
char *serial;
|
||||
unsigned int vendor_id;
|
||||
unsigned int product_id;
|
||||
char autoflush;
|
||||
FT_HANDLE fc;
|
||||
int outcount;
|
||||
unsigned char *outbuf;
|
||||
int outbuf_len;
|
||||
} ftd2xx_params_t;
|
||||
|
||||
static int ftd2xx_set_data ( parport_t *parport, uint8_t data );
|
||||
static int ftd2xx_set_control ( parport_t *parport, uint8_t data );
|
||||
static int ftd2xx_flush_output ( ftd2xx_params_t *p );
|
||||
|
||||
static parport_t *
|
||||
ftd2xx_parport_alloc( const char *vidpid, parport_driver_t * parport_driver, size_t outbuf_len )
|
||||
{
|
||||
ftd2xx_params_t *params = malloc( sizeof *params );
|
||||
parport_t *parport = malloc( sizeof *parport );
|
||||
port_node_t *node = malloc( sizeof *node );
|
||||
unsigned char *outbuf = malloc( outbuf_len );
|
||||
|
||||
if (!node || !parport || !params || !outbuf) {
|
||||
free( node );
|
||||
free( parport );
|
||||
free( params );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
params->outbuf = outbuf;
|
||||
params->outbuf_len = outbuf_len;
|
||||
params->outcount = 0;
|
||||
params->autoflush = 0;
|
||||
params->product_id = 0;
|
||||
params->vendor_id = 0;
|
||||
params->serial = NULL;
|
||||
|
||||
{
|
||||
char *f = strchr(vidpid, ':');
|
||||
char *l = strrchr(vidpid, ':');
|
||||
if(f)
|
||||
{
|
||||
params->vendor_id = strtoul(vidpid, NULL, 16);
|
||||
params->product_id = strtoul(f+1, NULL, 16);
|
||||
if(l!=f) params->serial = strdup(l+1);
|
||||
};
|
||||
};
|
||||
|
||||
parport->params = params;
|
||||
parport->driver = parport_driver;
|
||||
parport->cable = NULL;
|
||||
|
||||
node->port = parport;
|
||||
node->next = ports;
|
||||
|
||||
ports = node;
|
||||
|
||||
return parport;
|
||||
}
|
||||
|
||||
static void
|
||||
ftd2xx_parport_free( parport_t *port )
|
||||
{
|
||||
port_node_t **prev;
|
||||
|
||||
for (prev = &ports; *prev; prev = &((*prev)->next))
|
||||
if ((*prev)->port == port)
|
||||
break;
|
||||
|
||||
if (*prev) {
|
||||
port_node_t *pn = *prev;
|
||||
*prev = pn->next;
|
||||
free( pn );
|
||||
}
|
||||
|
||||
free( ((ftd2xx_params_t *) port->params)->serial );
|
||||
free( ((ftd2xx_params_t *) port->params)->outbuf );
|
||||
free( port->params );
|
||||
free( port );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ftd2xx_pre_connect( const char **par, int parnum )
|
||||
{
|
||||
port_node_t *pn;
|
||||
|
||||
if (parnum != 1) {
|
||||
printf( _("Syntax error!\n") );
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (pn = ports; pn; pn = pn->next)
|
||||
if (strcmp( pn->port->params, par[0] ) == 0) {
|
||||
printf( _("Disconnecting %s from FTDI device %s\n"), _(pn->port->cable->driver->description), par[0] );
|
||||
pn->port->cable->driver->disconnect( pn->port->cable );
|
||||
break;
|
||||
}
|
||||
|
||||
#if TODO
|
||||
printf( _("Initializing %s on FTDI device %s\n"), _(cable_drivers[i]->description), par[0] );
|
||||
#else
|
||||
printf( _("Initializing on FTDI device %s\n"), par[0] );
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
parport_t *
|
||||
ftd2xx_std_connect( const char **par, int parnum )
|
||||
{
|
||||
parport_t *parport;
|
||||
|
||||
if (!ftd2xx_pre_connect(par, parnum))
|
||||
return NULL;
|
||||
|
||||
parport = ftd2xx_parport_alloc( par[0], &ftd2xx_parport_driver, OUTBUF_LEN_STD );
|
||||
if (!parport) {
|
||||
printf( _("%s(%d) Out of memory.\n"), __FILE__, __LINE__ );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parport;
|
||||
}
|
||||
|
||||
|
||||
parport_t *
|
||||
ftd2xx_mpsse_connect( const char **par, int parnum )
|
||||
{
|
||||
parport_t *parport;
|
||||
|
||||
if (!ftd2xx_pre_connect(par, parnum))
|
||||
return NULL;
|
||||
|
||||
parport = ftd2xx_parport_alloc( par[0], &ftd2xx_mpsse_parport_driver, OUTBUF_LEN_MPSSE );
|
||||
if (!parport) {
|
||||
printf( _("%s(%d) Out of memory.\n"), __FILE__, __LINE__ );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parport;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftd2xx_generic_open( parport_t *parport )
|
||||
{
|
||||
ftd2xx_params_t *p = parport->params;
|
||||
FT_STATUS status;
|
||||
|
||||
#if !__CYGWIN__ && !__MINGW32__
|
||||
/* Add non-standard Vid/Pid to the linux driver */
|
||||
if ((status = FT_SetVIDPID(p->vendor_id, p->product_id)) != FT_OK)
|
||||
fprintf( stderr, "Warning: couldn't add %4.4x:%4.4x", p->vendor_id, p->product_id );
|
||||
#endif
|
||||
|
||||
if ((status = FT_Open(0, &(p->fc))) != FT_OK) {
|
||||
fprintf( stderr, "Error: unable to open FTDI device: %li\n", status);
|
||||
/* mark ftd2xx layer as not initialized */
|
||||
p->fc = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftd2xx_std_open( parport_t *parport )
|
||||
{
|
||||
int r;
|
||||
ftd2xx_params_t *p = parport->params;
|
||||
FT_HANDLE fc;
|
||||
FT_STATUS status;
|
||||
|
||||
r = ftd2xx_generic_open(parport);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fc = p->fc;
|
||||
|
||||
if ((status = FT_SetBitMode( fc, 0x00, 0x00 )) != FT_OK) {
|
||||
fprintf(stderr, "Can't disable bitmode: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_SetLatencyTimer(fc, 2)) != FT_OK) {
|
||||
fprintf(stderr, "Can't set latency timer: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_SetBaudRate(fc, 3E6)) != FT_OK) {
|
||||
fprintf(stderr, "Can't set baudrate: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftd2xx_mpsse_open( parport_t *parport )
|
||||
{
|
||||
int r;
|
||||
ftd2xx_params_t *p = parport->params;
|
||||
FT_HANDLE fc;
|
||||
FT_STATUS status;
|
||||
|
||||
r = ftd2xx_generic_open(parport);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fc = p->fc;
|
||||
|
||||
/* This sequence might seem weird and containing superfluous stuff.
|
||||
However, it's built after the description of JTAG_InitDevice
|
||||
Ref. FTCJTAGPG10.pdf
|
||||
Intermittent problems will occur when certain steps are skipped. */
|
||||
if ((status = FT_ResetDevice(fc)) != FT_OK) {
|
||||
fprintf(stderr, "Can't reset device: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_Purge(fc, FT_PURGE_RX)) != FT_OK) {
|
||||
fprintf(stderr, "Can't purge buffers: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_SetChars(fc, 0, 0, 0, 0)) != FT_OK) {
|
||||
fprintf(stderr, "Can't set special characters: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
/* set a reasonable latency timer value
|
||||
if this value is too low then the chip will send intermediate result data
|
||||
in short packets (suboptimal performance) */
|
||||
if ((status = FT_SetLatencyTimer(fc, 16)) != FT_OK) {
|
||||
fprintf(stderr, "Can't set target latency timer: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_SetBitMode( fc, 0x00, 0x00 )) != FT_OK) {
|
||||
fprintf(stderr, "Can't disable bitmode: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_SetBitMode( fc, 0x0b, 0x02 /*BITMODE_MPSSE*/ )) != FT_OK) {
|
||||
fprintf(stderr, "Can't set bitmode: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_ResetDevice(fc)) != FT_OK) {
|
||||
fprintf(stderr, "Can't reset device: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_Purge(fc, FT_PURGE_RX)) != FT_OK) {
|
||||
fprintf(stderr, "Can't purge buffers: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
/* set TCK Divisor */
|
||||
ftd2xx_set_data(parport, 0x86);
|
||||
ftd2xx_set_data(parport, 0x00);
|
||||
ftd2xx_set_data(parport, 0x00);
|
||||
ftd2xx_set_control(parport, 1);
|
||||
ftd2xx_set_control(parport, 0);
|
||||
/* switch off loopback */
|
||||
ftd2xx_set_data(parport, 0x85);
|
||||
ftd2xx_set_control(parport, 1);
|
||||
ftd2xx_set_control(parport, 0);
|
||||
if ((status = FT_ResetDevice(fc)) != FT_OK) {
|
||||
fprintf(stderr, "Can't reset device: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
if ((status = FT_Purge(fc, FT_PURGE_RX)) != FT_OK) {
|
||||
fprintf(stderr, "Can't purge buffers: %li\n", status);
|
||||
FT_Close(fc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftd2xx_flush_output ( ftd2xx_params_t *p )
|
||||
{
|
||||
DWORD xferred;
|
||||
FT_STATUS status;
|
||||
|
||||
if ((status = FT_Write( p->fc, p->outbuf, (DWORD)(p->outcount), &xferred )) != FT_OK)
|
||||
fprintf( stderr, "Error: FT_Write() failed in %i\n", __LINE__ );
|
||||
|
||||
if(xferred > 0 && xferred < p->outcount)
|
||||
{
|
||||
int offset = xferred;
|
||||
int remaining = p->outcount - xferred;
|
||||
|
||||
while(remaining)
|
||||
{
|
||||
printf("W\n");
|
||||
if(xferred < 0) return xferred;
|
||||
if ((status = FT_Write( p->fc, p->outbuf + offset, (DWORD)remaining, &xferred)) != FT_OK)
|
||||
fprintf( stderr, "Error: FT_Write() failed in %i\n", __LINE__ );
|
||||
if(xferred < 0)
|
||||
{
|
||||
memmove(p->outbuf, p->outbuf + offset, remaining);
|
||||
p->outcount = remaining;
|
||||
return 0;
|
||||
}
|
||||
offset += xferred;
|
||||
remaining -= xferred;
|
||||
}
|
||||
};
|
||||
p->outcount = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftd2xx_close( parport_t *parport )
|
||||
{
|
||||
ftd2xx_params_t *p = parport->params;
|
||||
|
||||
if (p->fc) {
|
||||
if(p->outcount > 0) ftd2xx_flush_output( p );
|
||||
p->outcount = 0;
|
||||
|
||||
FT_SetBitMode( p->fc, 0x00, 0x00 );
|
||||
FT_Close(p->fc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftd2xx_set_data( parport_t *parport, uint8_t data )
|
||||
{
|
||||
ftd2xx_params_t *p = parport->params;
|
||||
DWORD xferred;
|
||||
FT_STATUS status;
|
||||
|
||||
if (p->fc) {
|
||||
if(p->autoflush)
|
||||
{
|
||||
if ((status = FT_Write( p->fc, &data, 1 , &xferred)) != FT_OK)
|
||||
fprintf( stderr, "Error: FT_Write() failed in %i\n", __LINE__ );
|
||||
if (xferred != 1)
|
||||
printf("w\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
p->outbuf[p->outcount++] = data;
|
||||
|
||||
if(p->outcount >= p->outbuf_len)
|
||||
return ftd2xx_flush_output( p );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftd2xx_get_data( parport_t *parport )
|
||||
{
|
||||
DWORD xferred = 0;
|
||||
FT_STATUS status;
|
||||
unsigned char d;
|
||||
ftd2xx_params_t *p = parport->params;
|
||||
|
||||
if (p->fc) {
|
||||
while (xferred == 0) {
|
||||
if ((status = FT_Read( p->fc, &d, 1, &xferred )) != FT_OK)
|
||||
printf( "Error: FT_Read() failed in %i\n", __LINE__ );
|
||||
}
|
||||
return d;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftd2xx_get_status( parport_t *parport )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftd2xx_set_control( parport_t *parport, uint8_t data )
|
||||
{
|
||||
ftd2xx_params_t *p = parport->params;
|
||||
|
||||
if (p->fc) {
|
||||
p->autoflush = data;
|
||||
if(p->autoflush) ftd2xx_flush_output( p );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
parport_driver_t ftd2xx_parport_driver = {
|
||||
"ftd2xx",
|
||||
ftd2xx_std_connect,
|
||||
ftd2xx_parport_free,
|
||||
ftd2xx_std_open,
|
||||
ftd2xx_close,
|
||||
ftd2xx_set_data,
|
||||
ftd2xx_get_data,
|
||||
ftd2xx_get_status,
|
||||
ftd2xx_set_control
|
||||
};
|
||||
|
||||
parport_driver_t ftd2xx_mpsse_parport_driver = {
|
||||
"ftd2xx-mpsse",
|
||||
ftd2xx_mpsse_connect,
|
||||
ftd2xx_parport_free,
|
||||
ftd2xx_mpsse_open,
|
||||
ftd2xx_close,
|
||||
ftd2xx_set_data,
|
||||
ftd2xx_get_data,
|
||||
ftd2xx_get_status,
|
||||
ftd2xx_set_control
|
||||
};
|
@ -1,602 +0,0 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* libftdi Driver
|
||||
* Copyright (C) 2006 K. Waschk
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by Kolja Waschk, 2006.
|
||||
* Structure taken from ppdev.c, written by Marcel Telka, 2003.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sysdep.h"
|
||||
|
||||
#ifdef HAVE_LIBFTDI
|
||||
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_STROPTS_H
|
||||
#include <stropts.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ftdi.h>
|
||||
|
||||
#include "parport.h"
|
||||
#include "cable.h"
|
||||
|
||||
parport_driver_t ftdi_parport_driver;
|
||||
parport_driver_t ftdi_mpsse_parport_driver;
|
||||
|
||||
typedef struct port_node_t port_node_t;
|
||||
|
||||
struct port_node_t {
|
||||
parport_t *port;
|
||||
port_node_t *next;
|
||||
};
|
||||
|
||||
static port_node_t *ports = NULL; /* devices */
|
||||
|
||||
#define OUTBUF_LEN_STD 64
|
||||
#define OUTBUF_LEN_MPSSE 4096
|
||||
|
||||
typedef struct {
|
||||
char *serial;
|
||||
unsigned int vendor_id;
|
||||
unsigned int product_id;
|
||||
char autoflush;
|
||||
struct ftdi_context *fc;
|
||||
int outcount;
|
||||
unsigned char *outbuf;
|
||||
int outbuf_len;
|
||||
} ftdi_params_t;
|
||||
|
||||
static int ftdi_set_data ( parport_t *parport, uint8_t data );
|
||||
static int ftdi_set_control ( parport_t *parport, uint8_t data );
|
||||
static int ftdi_flush_output ( ftdi_params_t *p );
|
||||
|
||||
static parport_t *
|
||||
ftdi_parport_alloc( const char *vidpid, parport_driver_t * parport_driver, size_t outbuf_len )
|
||||
{
|
||||
ftdi_params_t *params = malloc( sizeof *params );
|
||||
parport_t *parport = malloc( sizeof *parport );
|
||||
port_node_t *node = malloc( sizeof *node );
|
||||
struct ftdi_context *fc = malloc( sizeof(struct ftdi_context) );
|
||||
unsigned char *outbuf = malloc( outbuf_len );
|
||||
|
||||
if (!node || !parport || !params || !fc || !outbuf) {
|
||||
free( node );
|
||||
free( parport );
|
||||
free( params );
|
||||
free( fc );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ftdi_init(fc);
|
||||
params->outbuf = outbuf;
|
||||
params->outbuf_len = outbuf_len;
|
||||
params->outcount = 0;
|
||||
params->autoflush = 0;
|
||||
params->product_id = 0;
|
||||
params->vendor_id = 0;
|
||||
params->serial = NULL;
|
||||
params->fc = fc;
|
||||
|
||||
{
|
||||
char *f = strchr(vidpid, ':');
|
||||
char *l = strrchr(vidpid, ':');
|
||||
if(f)
|
||||
{
|
||||
params->vendor_id = strtoul(vidpid, NULL, 16);
|
||||
params->product_id = strtoul(f+1, NULL, 16);
|
||||
if(l!=f) params->serial = strdup(l+1);
|
||||
};
|
||||
};
|
||||
|
||||
parport->params = params;
|
||||
parport->driver = parport_driver;
|
||||
parport->cable = NULL;
|
||||
|
||||
node->port = parport;
|
||||
node->next = ports;
|
||||
|
||||
ports = node;
|
||||
|
||||
return parport;
|
||||
}
|
||||
|
||||
static void
|
||||
ftdi_parport_free( parport_t *port )
|
||||
{
|
||||
port_node_t **prev;
|
||||
|
||||
for (prev = &ports; *prev; prev = &((*prev)->next))
|
||||
if ((*prev)->port == port)
|
||||
break;
|
||||
|
||||
if (*prev) {
|
||||
port_node_t *pn = *prev;
|
||||
*prev = pn->next;
|
||||
free( pn );
|
||||
}
|
||||
|
||||
free( ((ftdi_params_t *) port->params)->serial );
|
||||
free( ((ftdi_params_t *) port->params)->outbuf );
|
||||
free( ((ftdi_params_t *) port->params)->fc );
|
||||
free( port->params );
|
||||
free( port );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ftdi_pre_connect( const char **par, int parnum )
|
||||
{
|
||||
port_node_t *pn;
|
||||
|
||||
if (parnum != 1) {
|
||||
printf( _("Syntax error!\n") );
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (pn = ports; pn; pn = pn->next)
|
||||
if (strcmp( pn->port->params, par[0] ) == 0) {
|
||||
printf( _("Disconnecting %s from FTDI device %s\n"), _(pn->port->cable->driver->description), par[0] );
|
||||
pn->port->cable->driver->disconnect( pn->port->cable );
|
||||
break;
|
||||
}
|
||||
|
||||
#if TODO
|
||||
printf( _("Initializing %s on FTDI device %s\n"), _(cable_drivers[i]->description), par[0] );
|
||||
#else
|
||||
printf( _("Initializing on FTDI device %s\n"), par[0] );
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
parport_t *
|
||||
ftdi_std_connect( const char **par, int parnum )
|
||||
{
|
||||
parport_t *parport;
|
||||
|
||||
if(!ftdi_pre_connect(par, parnum))
|
||||
return NULL;
|
||||
|
||||
parport = ftdi_parport_alloc( par[0], &ftdi_parport_driver, OUTBUF_LEN_STD );
|
||||
if (!parport) {
|
||||
printf( _("%s(%d) Out of memory.\n"), __FILE__, __LINE__ );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parport;
|
||||
}
|
||||
|
||||
|
||||
parport_t *
|
||||
ftdi_mpsse_connect( const char **par, int parnum )
|
||||
{
|
||||
parport_t *parport;
|
||||
|
||||
if(!ftdi_pre_connect(par, parnum))
|
||||
return NULL;
|
||||
|
||||
parport = ftdi_parport_alloc( par[0], &ftdi_mpsse_parport_driver, OUTBUF_LEN_MPSSE );
|
||||
if (!parport) {
|
||||
printf( _("%s(%d) Out of memory.\n"), __FILE__, __LINE__ );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parport;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftdi_generic_open( parport_t *parport )
|
||||
{
|
||||
int r;
|
||||
ftdi_params_t *p = parport->params;
|
||||
struct ftdi_context *fc = p->fc;
|
||||
|
||||
/* Try to be intelligent about IDs */
|
||||
|
||||
if(p->vendor_id)
|
||||
r = ftdi_usb_open_desc(fc, p->vendor_id, p->product_id, NULL, p->serial); /* USB-Blaster */
|
||||
else
|
||||
{
|
||||
r = ftdi_usb_open_desc(fc, 0x09FB, 0x6001, NULL, p->serial); /* USB-Blaster */
|
||||
if(r<0) r = ftdi_usb_open_desc(fc, 0x09FB, 0x6002, NULL, p->serial); /* Cubic Cyclonium */
|
||||
if(r<0) r = ftdi_usb_open_desc(fc, 0x09FB, 0x6003, NULL, p->serial); /* NIOS II Evaluation board */
|
||||
if(r<0) r = ftdi_usb_open_desc(fc, 0x16C0, 0x06AD, NULL, p->serial); /* http://www.ixo.de/info/usb_jtag/ */
|
||||
if(r<0) r = ftdi_usb_open_desc(fc, 0x0403, 0xCFF8, NULL, p->serial); /* Amontec JTAGkey http://www.amontec.com/jtagkey.shtml */
|
||||
if(r<0) r = ftdi_usb_open_desc(fc, 0x15BA, 0x0003, NULL, p->serial); /* Olimex ARM-USB-OCD */
|
||||
if(r<0) r = ftdi_usb_open_desc(fc, 0x0403, 0xBDC8, NULL, p->serial); /* Turtelizer 2 */
|
||||
if(r<0) r = ftdi_usb_open_desc(fc, 0x0456, 0xF000, NULL, p->serial); /* Analog Devices BFIN-UJTAG */
|
||||
};
|
||||
|
||||
if(r<0)
|
||||
{
|
||||
fprintf (stderr, "Can't open ftdi device: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_deinit(fc);
|
||||
/* mark ftdi layer as not initialized */
|
||||
p->fc = NULL;
|
||||
return -1;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftdi_std_open( parport_t *parport )
|
||||
{
|
||||
int r;
|
||||
ftdi_params_t *p = parport->params;
|
||||
struct ftdi_context *fc = p->fc;
|
||||
|
||||
r = ftdi_generic_open(parport);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void)ftdi_disable_bitbang(fc);
|
||||
|
||||
if(ftdi_set_latency_timer(fc, 2)<0)
|
||||
{
|
||||
fprintf (stderr, "Can't set minimum latency: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
};
|
||||
|
||||
#if 1
|
||||
/* libftdi 0.6 doesn't allow high baudrates, so we send the control
|
||||
message outselves */
|
||||
|
||||
if (usb_control_msg(fc->usb_dev, 0x40, 3, 1, 0, NULL, 0, fc->usb_write_timeout) != 0)
|
||||
{
|
||||
fprintf (stderr, "Can't set max baud rate.\n");
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
};
|
||||
#else
|
||||
if(ftdi_set_baudrate(fc, 48000000)<0)
|
||||
{
|
||||
fprintf (stderr, "Can't set max baud rate: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
};
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#undef LIBFTDI_UNIMPLEMENTED
|
||||
|
||||
static int
|
||||
seq_purge( struct ftdi_context *fc, int purge_rx, int purge_tx )
|
||||
{
|
||||
int result = 0;
|
||||
unsigned char buf;
|
||||
|
||||
#ifndef LIBFTDI_UNIMPLEMENTED
|
||||
result = ftdi_usb_purge_buffers( fc );
|
||||
if (result == 0)
|
||||
result = ftdi_read_data( fc, &buf, 1 );
|
||||
#else /* not yet available */
|
||||
{
|
||||
int rx_loop;
|
||||
|
||||
if (purge_rx)
|
||||
for (rx_loop = 0; (rx_loop < 6) && (result == 0); rx_loop++)
|
||||
result = ftdi_usb_purge_rx_buffer( fc );
|
||||
|
||||
if (purge_tx)
|
||||
if (result == 0)
|
||||
result = ftdi_usb_purge_tx_buffer( fc );
|
||||
if (result == 0)
|
||||
ftdi_read_data( fc, &buf, 1 );
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
seq_reset( struct ftdi_context *fc )
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
#ifdef LIBFTDI_UNIMPLEMENTED /* not yet available */
|
||||
{
|
||||
unsigned short status;
|
||||
if ((result = ftdi_poll_status( fc, &status )) < 0)
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
if ((result = ftdi_usb_reset( fc)) < 0)
|
||||
return result;
|
||||
|
||||
return seq_purge( fc, 1, 1 );
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftdi_mpsse_open( parport_t *parport )
|
||||
{
|
||||
int r;
|
||||
ftdi_params_t *p = parport->params;
|
||||
struct ftdi_context *fc = p->fc;
|
||||
|
||||
r = ftdi_generic_open(parport);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* This sequence might seem weird and containing superfluous stuff.
|
||||
However, it's built after the description of JTAG_InitDevice
|
||||
Ref. FTCJTAGPG10.pdf
|
||||
Intermittent problems will occur when certain steps are skipped. */
|
||||
if (seq_reset(fc) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't reset USB: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
if (seq_purge(fc, 1, 0) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't purge USB RX buffer: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
#ifdef LIBFTDI_UNIMPLEMENTED /* not yet available */
|
||||
if (ftdi_set_chars(fc, 0, 0, 0, 0) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't set special characters: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
/* set a reasonable latency timer value
|
||||
if this value is too low then the chip will send intermediate result data
|
||||
in short packets (suboptimal performance) */
|
||||
if(ftdi_set_latency_timer(fc, 16) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't set target latency: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftdi_disable_bitbang(fc) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't reset bitmode: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_set_bitmode(fc, 0x0b, BITMODE_MPSSE) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't set mpsse mode: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftdi_usb_reset(fc) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't reset USB: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
if (seq_purge(fc, 1, 0) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't purge USB RX buffer: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
/* set TCK Divisor */
|
||||
ftdi_set_data(parport, TCK_DIVISOR);
|
||||
ftdi_set_data(parport, 0x00);
|
||||
ftdi_set_data(parport, 0x00);
|
||||
ftdi_set_control(parport, 1);
|
||||
ftdi_set_control(parport, 0);
|
||||
/* switch off loopback */
|
||||
ftdi_set_data(parport, LOOPBACK_END);
|
||||
ftdi_set_control(parport, 1);
|
||||
ftdi_set_control(parport, 0);
|
||||
if (ftdi_usb_reset(fc) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't reset USB: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
if (seq_purge(fc, 1, 0) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't purge USB RX buffer: %s\n",
|
||||
ftdi_get_error_string (fc));
|
||||
ftdi_usb_close(fc);
|
||||
ftdi_deinit(fc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ftdi_flush_output ( ftdi_params_t *p )
|
||||
{
|
||||
int xferred;
|
||||
//int i;
|
||||
//unsigned char * q;
|
||||
|
||||
// printf("ftdi_flush_output, length %d", p->outcount);
|
||||
// for (i=0, q=p->outbuf; i<p->outcount; i++, q++)
|
||||
// printf(" %01x", *q);
|
||||
// printf("\n");
|
||||
xferred = ftdi_write_data(p->fc, p->outbuf, p->outcount);
|
||||
if (xferred < 0)
|
||||
printf( _("Error from ftdi_write_data(): %d\n"), xferred);
|
||||
|
||||
if(xferred > 0 && xferred < p->outcount)
|
||||
{
|
||||
int offset = xferred;
|
||||
int remaining = p->outcount - xferred;
|
||||
|
||||
while(remaining)
|
||||
{
|
||||
printf("W\n");
|
||||
if(xferred < 0) return xferred;
|
||||
xferred = ftdi_write_data(p->fc, p->outbuf + offset, remaining);
|
||||
if(xferred < 0)
|
||||
{
|
||||
memmove(p->outbuf, p->outbuf + offset, remaining);
|
||||
p->outcount = remaining;
|
||||
return 0;
|
||||
}
|
||||
offset += xferred;
|
||||
remaining -= xferred;
|
||||
}
|
||||
};
|
||||
p->outcount = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftdi_close( parport_t *parport )
|
||||
{
|
||||
ftdi_params_t *p = parport->params;
|
||||
|
||||
if (p->fc) {
|
||||
if(p->outcount > 0) ftdi_flush_output( p );
|
||||
p->outcount = 0;
|
||||
|
||||
ftdi_disable_bitbang(p->fc);
|
||||
ftdi_usb_close(p->fc);
|
||||
ftdi_deinit(p->fc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftdi_set_data( parport_t *parport, uint8_t data )
|
||||
{
|
||||
ftdi_params_t *p = parport->params;
|
||||
|
||||
if (p->fc) {
|
||||
if(p->autoflush)
|
||||
{
|
||||
if(ftdi_write_data(p->fc, &data, 1) != 1) printf("w\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
p->outbuf[p->outcount++] = data;
|
||||
|
||||
if(p->outcount >= p->outbuf_len)
|
||||
return ftdi_flush_output( p );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftdi_get_data( parport_t *parport )
|
||||
{
|
||||
unsigned char d;
|
||||
ftdi_params_t *p = parport->params;
|
||||
int res = 0;
|
||||
|
||||
if (p->fc) {
|
||||
while(ftdi_read_data( p->fc, &d, 1) == 0);
|
||||
res = d;
|
||||
} else
|
||||
res = 0;
|
||||
// printf("ftdi_get_data %01x\n", d);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
ftdi_get_status( parport_t *parport )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftdi_set_control( parport_t *parport, uint8_t data )
|
||||
{
|
||||
ftdi_params_t *p = parport->params;
|
||||
|
||||
if (p->fc) {
|
||||
p->autoflush = data;
|
||||
if(p->autoflush) ftdi_flush_output( p );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
parport_driver_t ftdi_parport_driver = {
|
||||
"ftdi",
|
||||
ftdi_std_connect,
|
||||
ftdi_parport_free,
|
||||
ftdi_std_open,
|
||||
ftdi_close,
|
||||
ftdi_set_data,
|
||||
ftdi_get_data,
|
||||
ftdi_get_status,
|
||||
ftdi_set_control
|
||||
};
|
||||
|
||||
parport_driver_t ftdi_mpsse_parport_driver = {
|
||||
"ftdi-mpsse",
|
||||
ftdi_mpsse_connect,
|
||||
ftdi_parport_free,
|
||||
ftdi_mpsse_open,
|
||||
ftdi_close,
|
||||
ftdi_set_data,
|
||||
ftdi_get_data,
|
||||
ftdi_get_status,
|
||||
ftdi_set_control
|
||||
};
|
||||
|
||||
#endif /* HAVE_LIBFTDI */
|
@ -0,0 +1,482 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Link driver for accessing FTDI devices via libftd2xx
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by Arnim Laeuger, 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sysdep.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#if __CYGWIN__ || __MINGW32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STROPTS_H
|
||||
#include <stropts.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ftd2xx.h>
|
||||
|
||||
#include "cable.h"
|
||||
#include "usbconn.h"
|
||||
#include "usbconn/libftdx.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* USB device information */
|
||||
unsigned int vid;
|
||||
unsigned int pid;
|
||||
FT_HANDLE fc;
|
||||
char *serial;
|
||||
/* send and receive buffer handling */
|
||||
uint32_t send_buf_len;
|
||||
uint32_t send_buffered;
|
||||
uint8_t *send_buf;
|
||||
uint32_t recv_buf_len;
|
||||
uint32_t to_recv;
|
||||
uint32_t recv_write_idx;
|
||||
uint32_t recv_read_idx;
|
||||
uint8_t *recv_buf;
|
||||
} ftd2xx_param_t;
|
||||
|
||||
usbconn_driver_t usbconn_ftd2xx_driver;
|
||||
usbconn_driver_t usbconn_ftd2xx_mpsse_driver;
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftd2xx_flush( ftd2xx_param_t *p )
|
||||
{
|
||||
FT_STATUS status;
|
||||
DWORD xferred;
|
||||
DWORD recvd = 0;
|
||||
|
||||
if (!p->fc)
|
||||
return -1;
|
||||
|
||||
if (p->send_buffered == 0)
|
||||
return 0;
|
||||
|
||||
if ((status = FT_Write( p->fc, p->send_buf, p->send_buffered, &xferred )) != FT_OK)
|
||||
perror( _("usbconn_ftd2xx_flush(): FT_Write() failed.\n") );
|
||||
|
||||
if (xferred < p->send_buffered)
|
||||
{
|
||||
perror( _("usbconn_ftd2xx_flush(): Written fewer bytes than requested.\n") );
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->send_buffered = 0;
|
||||
|
||||
/* now read all scheduled receive bytes */
|
||||
if (p->to_recv)
|
||||
{
|
||||
if (p->recv_write_idx + p->to_recv > p->recv_buf_len)
|
||||
{
|
||||
/* extend receive buffer */
|
||||
p->recv_buf_len = p->recv_write_idx + p->to_recv;
|
||||
if (p->recv_buf)
|
||||
p->recv_buf = (uint8_t *)realloc( p->recv_buf, p->recv_buf_len );
|
||||
}
|
||||
|
||||
if (!p->recv_buf)
|
||||
{
|
||||
perror( _("usbconn_ftd2xx_flush(): Receive buffer does not exist.\n") );
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (recvd == 0)
|
||||
if ((status = FT_Read( p->fc, &(p->recv_buf[p->recv_write_idx]),
|
||||
p->to_recv, &recvd )) != FT_OK)
|
||||
printf( _("usbconn_ftd2xx_flush(): Error from FT_Read(): %d\n"), (int)status );
|
||||
|
||||
if (recvd < p->to_recv)
|
||||
printf( _("usbconn_ftd2xx_flush(): Received less bytes than requested.\n") );
|
||||
|
||||
p->to_recv -= recvd;
|
||||
p->recv_write_idx += recvd;
|
||||
}
|
||||
|
||||
return status != FT_OK ? -1 : xferred;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftd2xx_read( usbconn_t *conn, uint8_t *buf, int len )
|
||||
{
|
||||
ftd2xx_param_t *p = conn->params;
|
||||
int cpy_len;
|
||||
FT_STATUS status = FT_OK;
|
||||
DWORD recvd = 0;
|
||||
|
||||
if (!p->fc)
|
||||
return -1;
|
||||
|
||||
/* flush send buffer to get all scheduled receive bytes */
|
||||
if (usbconn_ftd2xx_flush( p ) < 0)
|
||||
return -1;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/* check for number of remaining bytes in receive buffer */
|
||||
cpy_len = p->recv_write_idx - p->recv_read_idx;
|
||||
if (cpy_len > len)
|
||||
cpy_len = len;
|
||||
len -= cpy_len;
|
||||
|
||||
if (cpy_len > 0)
|
||||
{
|
||||
/* get data from the receive buffer */
|
||||
memcpy( buf, &(p->recv_buf[p->recv_read_idx]), cpy_len );
|
||||
p->recv_read_idx += cpy_len;
|
||||
if (p->recv_read_idx == p->recv_write_idx)
|
||||
p->recv_read_idx = p->recv_write_idx = 0;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
/* need to get more data directly from the device */
|
||||
while (recvd == 0)
|
||||
if ((status = FT_Read( p->fc, &(buf[cpy_len]), len, &recvd )) != FT_OK)
|
||||
printf( _("usbconn_ftd2xx_read(): Error from FT_Read(): %d\n"), (int)status );
|
||||
}
|
||||
|
||||
return status != FT_OK ? -1 : cpy_len + len;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftd2xx_write( usbconn_t *conn, uint8_t *buf, int len, int recv )
|
||||
{
|
||||
ftd2xx_param_t *p = conn->params;
|
||||
int xferred = 0;
|
||||
|
||||
if (!p->fc)
|
||||
return -1;
|
||||
|
||||
/* this write function will try to buffer write data
|
||||
buffering will be ceased and a flush triggered in two cases. */
|
||||
|
||||
/* Case A: max number of scheduled receive bytes will be exceeded
|
||||
with this write
|
||||
Case B: max number of scheduled send bytes has been reached */
|
||||
if ((p->to_recv + recv > FTD2XX_MAXRECV)
|
||||
|| ((p->send_buffered > FTDX_MAXSEND) && (p->to_recv == 0)))
|
||||
xferred = usbconn_ftd2xx_flush( p );
|
||||
|
||||
if (xferred < 0)
|
||||
return -1;
|
||||
|
||||
/* now buffer this write */
|
||||
if (p->send_buffered + len > p->send_buf_len)
|
||||
{
|
||||
p->send_buf_len = p->send_buffered + len;
|
||||
if (p->send_buf)
|
||||
p->send_buf = (uint8_t *)realloc( p->send_buf, p->send_buf_len);
|
||||
}
|
||||
|
||||
if (p->send_buf)
|
||||
{
|
||||
memcpy( &(p->send_buf[p->send_buffered]), buf, len );
|
||||
p->send_buffered += len;
|
||||
p->to_recv += recv;
|
||||
|
||||
if (recv < 0)
|
||||
{
|
||||
/* immediate write requested, so flush the buffered data */
|
||||
xferred = usbconn_ftd2xx_flush( p );
|
||||
}
|
||||
|
||||
return xferred < 0 ? -1 : len;
|
||||
}
|
||||
else
|
||||
{
|
||||
perror( _("usbconn_ftd2xx_write(): Send buffer does not exist.\n") );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
usbconn_t *
|
||||
usbconn_ftd2xx_connect( const char **param, int paramc, usbconn_cable_t *template )
|
||||
{
|
||||
usbconn_t *c = malloc( sizeof( usbconn_t ) );
|
||||
ftd2xx_param_t *p = malloc( sizeof( ftd2xx_param_t ) );
|
||||
|
||||
if (p)
|
||||
{
|
||||
p->send_buf_len = FTDX_MAXSEND;
|
||||
p->send_buffered = 0;
|
||||
p->send_buf = (uint8_t *)malloc( p->send_buf_len );
|
||||
p->recv_buf_len = FTD2XX_MAXRECV;
|
||||
p->to_recv = 0;
|
||||
p->recv_write_idx = 0;
|
||||
p->recv_read_idx = 0;
|
||||
p->recv_buf = (uint8_t *)malloc( p->recv_buf_len );
|
||||
}
|
||||
|
||||
if (!p || !c || !p->send_buf || !p->recv_buf)
|
||||
{
|
||||
printf( _("Out of memory\n") );
|
||||
if (p->send_buf)
|
||||
free( p->send_buf );
|
||||
if (p->recv_buf)
|
||||
free( p->recv_buf );
|
||||
if (p)
|
||||
free( p );
|
||||
if (c)
|
||||
free( c );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p->fc = NULL;
|
||||
p->pid = template->pid;
|
||||
p->vid = template->vid;
|
||||
p->serial = NULL;
|
||||
|
||||
c->params = p;
|
||||
c->driver = &usbconn_ftd2xx_driver;
|
||||
c->cable = NULL;
|
||||
|
||||
printf( _("Connected to libftd2xx driver.\n") );
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
usbconn_t *
|
||||
usbconn_ftd2xx_mpsse_connect( const char **param, int paramc, usbconn_cable_t *template )
|
||||
{
|
||||
usbconn_t *conn = usbconn_ftd2xx_connect( param, paramc, template );
|
||||
|
||||
if (conn)
|
||||
conn->driver = &usbconn_ftd2xx_mpsse_driver;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftd2xx_common_open( usbconn_t *conn )
|
||||
{
|
||||
ftd2xx_param_t *p = conn->params;
|
||||
FT_STATUS status;
|
||||
|
||||
#if !__CYGWIN__ && !__MINGW32__
|
||||
/* Add non-standard Vid/Pid to the linux driver */
|
||||
if ((status = FT_SetVIDPID( p->vid, p->pid )) != FT_OK)
|
||||
fprintf( stderr, "Warning: couldn't add %4.4x:%4.4x", p->vid, p->pid );
|
||||
#endif
|
||||
|
||||
if ((status = FT_Open( 0, &(p->fc) )) != FT_OK)
|
||||
{
|
||||
perror( "Unable to open FTDO device.\n" );
|
||||
/* mark ftd2xx layer as not initialized */
|
||||
p->fc = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftd2xx_open( usbconn_t *conn )
|
||||
{
|
||||
ftd2xx_param_t *p = conn->params;
|
||||
FT_HANDLE fc;
|
||||
FT_STATUS status;
|
||||
|
||||
if (usbconn_ftd2xx_common_open( conn ) < 0)
|
||||
return -1;
|
||||
|
||||
fc = p->fc;
|
||||
|
||||
if ((status = FT_SetBitMode( fc, 0x00, 0x00 )) != FT_OK)
|
||||
perror( _("Can't disable bitmode.\n") );
|
||||
|
||||
if (status == FT_OK) if ((status = FT_SetLatencyTimer(fc, 2)) != FT_OK)
|
||||
perror( _("Can't set latency timer.\n") );
|
||||
|
||||
if (status == FT_OK) if ((status = FT_SetBaudRate(fc, 3E6)) != FT_OK)
|
||||
perror( _("Can't set baudrate.\n") );
|
||||
|
||||
if (status != FT_OK)
|
||||
{
|
||||
FT_Close( fc );
|
||||
/* mark ftdi layer as not initialized */
|
||||
p->fc = NULL;
|
||||
}
|
||||
|
||||
return status != FT_OK ? -1 : 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftd2xx_mpsse_open( usbconn_t *conn )
|
||||
{
|
||||
ftd2xx_param_t *p = conn->params;
|
||||
FT_HANDLE fc;
|
||||
FT_STATUS status;
|
||||
|
||||
if (usbconn_ftd2xx_common_open( conn ) < 0)
|
||||
return -1;
|
||||
|
||||
fc = p->fc;
|
||||
|
||||
/* This sequence might seem weird and containing superfluous stuff.
|
||||
However, it's built after the description of JTAG_InitDevice
|
||||
Ref. FTCJTAGPG10.pdf
|
||||
Intermittent problems will occur when certain steps are skipped. */
|
||||
if ((status = FT_ResetDevice( fc )) != FT_OK)
|
||||
perror( _("Can't reset device.\n") );
|
||||
if (status == FT_OK) if ((status = FT_Purge( fc, FT_PURGE_RX )) != FT_OK)
|
||||
perror( _("Can't purge RX buffer.\n") );
|
||||
|
||||
if (status == FT_OK) if ((status = FT_SetChars( fc, 0, 0, 0, 0 )) != FT_OK)
|
||||
perror( _("Can't set special characters.\n") );
|
||||
|
||||
/* set a reasonable latency timer value
|
||||
if this value is too low then the chip will send intermediate result data
|
||||
in short packets (suboptimal performance) */
|
||||
if (status == FT_OK) if ((status = FT_SetLatencyTimer( fc, 16 )) != FT_OK)
|
||||
perror( _("Can't set target latency timer.\n") );
|
||||
|
||||
if (status == FT_OK) if ((status = FT_SetBitMode( fc, 0x00, 0x00 )) != FT_OK)
|
||||
perror( _("Can't disable bitmode.\n") );
|
||||
if (status == FT_OK) if ((status = FT_SetBitMode( fc, 0x0b, 0x02 /* BITMODE_MPSSE */ )) != FT_OK)
|
||||
perror( _("Can't set MPSSE bitmode.\n") );
|
||||
|
||||
if (status == FT_OK) if ((status = FT_ResetDevice( fc )) != FT_OK)
|
||||
perror( _("Can't reset device.\n") );
|
||||
if (status == FT_OK) if ((status = FT_Purge( fc, FT_PURGE_RX )) != FT_OK)
|
||||
perror( _("Can't purge RX buffer.\n") );
|
||||
|
||||
/* set TCK Divisor */
|
||||
if (status == FT_OK)
|
||||
{
|
||||
uint8_t buf[3] = {0x86, 0x00, 0x00};
|
||||
if (usbconn_ftd2xx_write( conn, buf, 3, 0 ) < 0)
|
||||
status = FT_OTHER_ERROR;
|
||||
}
|
||||
/* switch off loopback */
|
||||
if (status == FT_OK)
|
||||
{
|
||||
uint8_t buf[1] = {0x85};
|
||||
if (usbconn_ftd2xx_write( conn, buf, 1, 0 ) < 0)
|
||||
status = FT_OTHER_ERROR;
|
||||
}
|
||||
if (status == FT_OK)
|
||||
if (usbconn_ftd2xx_read( conn, NULL, 0 ) < 0)
|
||||
status = FT_OTHER_ERROR;
|
||||
|
||||
if (status == FT_OK) if ((status = FT_ResetDevice( fc )) != FT_OK)
|
||||
perror( _("Can't reset device.\n") );
|
||||
if (status == FT_OK) if ((status = FT_Purge( fc, FT_PURGE_RX )) != FT_OK)
|
||||
perror( _("Can't purge RX buffer.\n") );
|
||||
|
||||
if (status != FT_OK)
|
||||
{
|
||||
FT_Close( fc );
|
||||
/* mark ftdi layer as not initialized */
|
||||
p->fc = NULL;
|
||||
}
|
||||
|
||||
return status != FT_OK ? -1 : 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftd2xx_close( usbconn_t *conn )
|
||||
{
|
||||
ftd2xx_param_t *p = conn->params;
|
||||
|
||||
if (p->fc)
|
||||
{
|
||||
FT_SetBitMode( p->fc, 0x00, 0x00 );
|
||||
FT_Close( p->fc );
|
||||
p->fc = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
usbconn_ftd2xx_free( usbconn_t *conn )
|
||||
{
|
||||
ftd2xx_param_t *p = conn->params;
|
||||
|
||||
if (p->send_buf)
|
||||
free( p->send_buf );
|
||||
if (p->recv_buf)
|
||||
free( p->recv_buf );
|
||||
if (p->serial)
|
||||
free( p->serial );
|
||||
|
||||
free( conn->params );
|
||||
free( conn );
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
usbconn_driver_t usbconn_ftd2xx_driver = {
|
||||
"ftd2xx",
|
||||
usbconn_ftd2xx_connect,
|
||||
usbconn_ftd2xx_free,
|
||||
usbconn_ftd2xx_open,
|
||||
usbconn_ftd2xx_close,
|
||||
usbconn_ftd2xx_read,
|
||||
usbconn_ftd2xx_write
|
||||
};
|
||||
|
||||
usbconn_driver_t usbconn_ftd2xx_mpsse_driver = {
|
||||
"ftd2xx-mpsse",
|
||||
usbconn_ftd2xx_mpsse_connect,
|
||||
usbconn_ftd2xx_free,
|
||||
usbconn_ftd2xx_mpsse_open,
|
||||
usbconn_ftd2xx_close,
|
||||
usbconn_ftd2xx_read,
|
||||
usbconn_ftd2xx_write
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:C
|
||||
c-default-style:gnu
|
||||
indent-tabs-mode:nil
|
||||
End:
|
||||
*/
|
@ -0,0 +1,542 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Link driver for accessing FTDI devices via libftdi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by Arnim Laeuger, 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sysdep.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#if __CYGWIN__ || __MINGW32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STROPTS_H
|
||||
#include <stropts.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ftdi.h>
|
||||
|
||||
#include "cable.h"
|
||||
#include "usbconn.h"
|
||||
#include "usbconn/libftdx.h"
|
||||
|
||||
typedef struct {
|
||||
/* USB device information */
|
||||
unsigned int vid;
|
||||
unsigned int pid;
|
||||
struct ftdi_context *fc;
|
||||
char *serial;
|
||||
/* send and receive buffer handling */
|
||||
uint32_t send_buf_len;
|
||||
uint32_t send_buffered;
|
||||
uint8_t *send_buf;
|
||||
uint32_t recv_buf_len;
|
||||
uint32_t to_recv;
|
||||
uint32_t recv_write_idx;
|
||||
uint32_t recv_read_idx;
|
||||
uint8_t *recv_buf;
|
||||
} ftdi_param_t;
|
||||
|
||||
usbconn_driver_t usbconn_ftdi_driver;
|
||||
usbconn_driver_t usbconn_ftdi_mpsse_driver;
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftdi_flush( ftdi_param_t *p )
|
||||
{
|
||||
int xferred;
|
||||
int recvd = 0;
|
||||
|
||||
if (!p->fc)
|
||||
return -1;
|
||||
|
||||
if (p->send_buffered == 0)
|
||||
return 0;
|
||||
|
||||
if ((xferred = ftdi_write_data( p->fc, p->send_buf, p->send_buffered )) < 0)
|
||||
perror( ftdi_get_error_string( p->fc ) );
|
||||
|
||||
if (xferred < p->send_buffered)
|
||||
{
|
||||
perror( _("usbconn_ftdi_flush(): Written fewer bytes than requested.\n") );
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->send_buffered = 0;
|
||||
|
||||
/* now read all scheduled receive bytes */
|
||||
if (p->to_recv)
|
||||
{
|
||||
if (p->recv_write_idx + p->to_recv > p->recv_buf_len)
|
||||
{
|
||||
/* extend receive buffer */
|
||||
p->recv_buf_len = p->recv_write_idx + p->to_recv;
|
||||
if (p->recv_buf)
|
||||
p->recv_buf = (uint8_t *)realloc( p->recv_buf, p->recv_buf_len );
|
||||
}
|
||||
|
||||
if (!p->recv_buf)
|
||||
{
|
||||
perror( _("usbconn_ftdi_flush(): Receive buffer does not exist.\n") );
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (recvd == 0)
|
||||
if ((recvd = ftdi_read_data( p->fc, &(p->recv_buf[p->recv_write_idx]),
|
||||
p->to_recv )) < 0)
|
||||
printf( _("usbconn_ftdi_flush(): Error from ftdi_read_data(): %s\n"),
|
||||
ftdi_get_error_string( p->fc ) );
|
||||
|
||||
if (recvd < p->to_recv)
|
||||
printf( _("usbconn_ftdi_flush(): Received less bytes than requested.\n") );
|
||||
|
||||
p->to_recv -= recvd;
|
||||
p->recv_write_idx += recvd;
|
||||
}
|
||||
|
||||
return xferred < 0 ? -1 : xferred;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftdi_read( usbconn_t *conn, uint8_t *buf, int len )
|
||||
{
|
||||
ftdi_param_t *p = conn->params;
|
||||
int cpy_len;
|
||||
int recvd = 0;
|
||||
|
||||
if (!p->fc)
|
||||
return -1;
|
||||
|
||||
/* flush send buffer to get all scheduled receive bytes */
|
||||
if (usbconn_ftdi_flush( p ) < 0)
|
||||
return -1;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/* check for number of remaining bytes in receive buffer */
|
||||
cpy_len = p->recv_write_idx - p->recv_read_idx;
|
||||
if (cpy_len > len)
|
||||
cpy_len = len;
|
||||
len -= cpy_len;
|
||||
|
||||
if (cpy_len > 0)
|
||||
{
|
||||
/* get data from the receive buffer */
|
||||
memcpy( buf, &(p->recv_buf[p->recv_read_idx]), cpy_len );
|
||||
p->recv_read_idx += cpy_len;
|
||||
if (p->recv_read_idx == p->recv_write_idx)
|
||||
p->recv_read_idx = p->recv_write_idx = 0;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
/* need to get more data directly from the device */
|
||||
while (recvd == 0)
|
||||
if ((recvd = ftdi_read_data( p->fc, &(buf[cpy_len]), len )) < 0)
|
||||
printf( _("usbconn_ftdi_read(): Error from ftdi_read_data(): %s\n"),
|
||||
ftdi_get_error_string( p->fc ) );
|
||||
}
|
||||
|
||||
return recvd < 0 ? -1 : cpy_len + len;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftdi_write( usbconn_t *conn, uint8_t *buf, int len, int recv )
|
||||
{
|
||||
ftdi_param_t *p = conn->params;
|
||||
int xferred = 0;
|
||||
|
||||
if (!p->fc)
|
||||
return -1;
|
||||
|
||||
/* this write function will try to buffer write data
|
||||
buffering will be ceased and a flush triggered in two cases. */
|
||||
|
||||
/* Case A: max number of scheduled receive bytes will be exceeded
|
||||
with this write
|
||||
Case B: max number of scheduled send bytes has been reached */
|
||||
if ((p->to_recv + recv > FTDI_MAXRECV)
|
||||
|| ((p->send_buffered > FTDX_MAXSEND) && (p->to_recv == 0)))
|
||||
xferred = usbconn_ftdi_flush( p );
|
||||
|
||||
if (xferred < 0)
|
||||
return -1;
|
||||
|
||||
/* now buffer this write */
|
||||
if (p->send_buffered + len > p->send_buf_len)
|
||||
{
|
||||
p->send_buf_len = p->send_buffered + len;
|
||||
if (p->send_buf)
|
||||
p->send_buf = (uint8_t *)realloc( p->send_buf, p->send_buf_len);
|
||||
}
|
||||
|
||||
if (p->send_buf)
|
||||
{
|
||||
memcpy( &(p->send_buf[p->send_buffered]), buf, len );
|
||||
p->send_buffered += len;
|
||||
p->to_recv += recv;
|
||||
|
||||
if (recv < 0)
|
||||
{
|
||||
/* immediate write requested, so flush the buffered data */
|
||||
xferred = usbconn_ftdi_flush( p );
|
||||
}
|
||||
|
||||
return xferred < 0 ? -1 : len;
|
||||
}
|
||||
else
|
||||
{
|
||||
perror( _("usbconn_ftdi_write(): Send buffer does not exist.\n") );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
usbconn_t *
|
||||
usbconn_ftdi_connect( const char **param, int paramc, usbconn_cable_t *template )
|
||||
{
|
||||
usbconn_t *c = malloc( sizeof( usbconn_t ) );
|
||||
ftdi_param_t *p = malloc( sizeof( ftdi_param_t ) );
|
||||
struct ftdi_context *fc = malloc( sizeof( struct ftdi_context ) );
|
||||
|
||||
if (p)
|
||||
{
|
||||
p->send_buf_len = FTDX_MAXSEND;
|
||||
p->send_buffered = 0;
|
||||
p->send_buf = (uint8_t *)malloc( p->send_buf_len );
|
||||
p->recv_buf_len = FTDI_MAXRECV;
|
||||
p->to_recv = 0;
|
||||
p->recv_write_idx = 0;
|
||||
p->recv_read_idx = 0;
|
||||
p->recv_buf = (uint8_t *)malloc( p->recv_buf_len );
|
||||
}
|
||||
|
||||
if (!p || !c || !fc || !p->send_buf || !p->recv_buf)
|
||||
{
|
||||
printf( _("Out of memory\n") );
|
||||
if (p->send_buf)
|
||||
free( p->send_buf );
|
||||
if (p->recv_buf)
|
||||
free( p->recv_buf );
|
||||
if (p)
|
||||
free( p );
|
||||
if (c)
|
||||
free( c );
|
||||
if (fc)
|
||||
free( fc );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ftdi_init( fc );
|
||||
p->fc = fc;
|
||||
p->pid = template->pid;
|
||||
p->vid = template->vid;
|
||||
p->serial = NULL;
|
||||
|
||||
c->params = p;
|
||||
c->driver = &usbconn_ftdi_driver;
|
||||
c->cable = NULL;
|
||||
|
||||
printf( _("Connected to libftdi driver.\n") );
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
usbconn_t *
|
||||
usbconn_ftdi_mpsse_connect( const char **param, int paramc, usbconn_cable_t *template )
|
||||
{
|
||||
usbconn_t *conn = usbconn_ftdi_connect( param, paramc, template );
|
||||
|
||||
if (conn)
|
||||
conn->driver = &usbconn_ftdi_mpsse_driver;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftdi_common_open( usbconn_t *conn )
|
||||
{
|
||||
ftdi_param_t *p = conn->params;
|
||||
struct ftdi_context *fc = p->fc;
|
||||
|
||||
if (ftdi_usb_open_desc( fc, p->vid, p->pid, NULL, p->serial ) < 0)
|
||||
{
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
ftdi_deinit( fc );
|
||||
/* mark ftdi layer as not initialized */
|
||||
p->fc = NULL;
|
||||
|
||||
/* TODO: disconnect? */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftdi_open( usbconn_t *conn )
|
||||
{
|
||||
ftdi_param_t *p = conn->params;
|
||||
struct ftdi_context *fc = p->fc;
|
||||
int r;
|
||||
|
||||
if (usbconn_ftdi_common_open( conn ) < 0)
|
||||
return -1;
|
||||
|
||||
if ((r = ftdi_disable_bitbang( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
|
||||
if (r >= 0) if ((r = ftdi_set_latency_timer( fc, 2 )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
|
||||
#if 1
|
||||
/* libftdi 0.6 doesn't allow high baudrates, so we send the control
|
||||
message outselves */
|
||||
if (r >= 0) if (usb_control_msg( fc->usb_dev, 0x40, 3, 1, 0, NULL, 0, fc->usb_write_timeout ) != 0)
|
||||
{
|
||||
perror( "Can't set max baud rate.\n" );
|
||||
r = -1;
|
||||
}
|
||||
#else
|
||||
if (r >= 0) if ((r = ftdi_set_baudrate( fc, 48000000 )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
#endif
|
||||
|
||||
if (r < 0)
|
||||
{
|
||||
ftdi_usb_close( fc );
|
||||
ftdi_deinit( fc );
|
||||
/* mark ftdi layer as not initialized */
|
||||
p->fc = NULL;
|
||||
}
|
||||
|
||||
return r < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#undef LIBFTDI_UNIMPLEMENTED
|
||||
|
||||
static int
|
||||
seq_purge( struct ftdi_context *fc, int purge_rx, int purge_tx )
|
||||
{
|
||||
int r;
|
||||
unsigned char buf;
|
||||
|
||||
#ifndef LIBFTDI_UNIMPLEMENTED
|
||||
if ((r = ftdi_usb_purge_buffers( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
if (r >= 0) if ((r = ftdi_read_data( fc, &buf, 1 )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
#else /* not yet available */
|
||||
{
|
||||
int rx_loop;
|
||||
|
||||
if (purge_rx)
|
||||
for (rx_loop = 0; (rx_loop < 6) && (r >= 0); rx_loop++)
|
||||
if ((r = ftdi_usb_purge_rx_buffer( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
|
||||
if (purge_tx)
|
||||
if (r >= 0) if ((r = ftdi_usb_purge_tx_buffer( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
if (r >= 0) if ((r = ftdi_read_data( fc, &buf, 1 )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
return r < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
seq_reset( struct ftdi_context *fc )
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef LIBFTDI_UNIMPLEMENTED /* not yet available */
|
||||
{
|
||||
unsigned short status;
|
||||
if ((r = ftdi_poll_status( fc, &status )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
}
|
||||
#endif
|
||||
if ((r = ftdi_usb_reset( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
|
||||
if (r >= 0) r = seq_purge( fc, 1, 1 );
|
||||
return r < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
usbconn_ftdi_mpsse_open( usbconn_t *conn )
|
||||
{
|
||||
ftdi_param_t *p = conn->params;
|
||||
struct ftdi_context *fc = p->fc;
|
||||
int r;
|
||||
|
||||
if (usbconn_ftdi_common_open( conn ) < 0)
|
||||
return -1;
|
||||
|
||||
/* This sequence might seem weird and containing superfluous stuff.
|
||||
However, it's built after the description of JTAG_InitDevice
|
||||
Ref. FTCJTAGPG10.pdf
|
||||
Intermittent problems will occur when certain steps are skipped. */
|
||||
r = seq_reset( fc );
|
||||
if (r >= 0) r = seq_purge( fc, 1, 0 );
|
||||
|
||||
#ifdef LIBFTDI_UNIMPLEMENTED
|
||||
if (r >= 0) if ((r = ftdi_set_event_char( fc, 0, 0 )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
if (r >= 0) if ((r = ftdi_set_error_char( fc, 0, 0 )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
#endif
|
||||
|
||||
/* set a reasonable latency timer value
|
||||
if this value is too low then the chip will send intermediate result data
|
||||
in short packets (suboptimal performance) */
|
||||
if (r >= 0) if ((r = ftdi_set_latency_timer( fc, 16 )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
|
||||
if (r >= 0) if ((r = ftdi_disable_bitbang( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
|
||||
if (r >= 0) if ((r = ftdi_set_bitmode( fc, 0x0b, BITMODE_MPSSE )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
|
||||
if (r >= 0) if ((r = ftdi_usb_reset( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
if (r >= 0) r = seq_purge( fc, 1, 0 );
|
||||
|
||||
/* set TCK Divisor */
|
||||
if (r >= 0)
|
||||
{
|
||||
uint8_t buf[3] = {TCK_DIVISOR, 0x00, 0x00};
|
||||
r = usbconn_ftdi_write( conn, buf, 3, 0 );
|
||||
}
|
||||
/* switch off loopback */
|
||||
if (r >= 0)
|
||||
{
|
||||
uint8_t buf[1] = {LOOPBACK_END};
|
||||
r = usbconn_ftdi_write( conn, buf, 1, 0 );
|
||||
}
|
||||
if (r >= 0) r = usbconn_ftdi_read( conn, NULL, 0 );
|
||||
|
||||
if (r >= 0) if ((r = ftdi_usb_reset( fc )) < 0)
|
||||
perror( ftdi_get_error_string( fc ) );
|
||||
if (r >= 0) r = seq_purge( fc, 1, 0 );
|
||||
|
||||
if (r < 0)
|
||||
{
|
||||
ftdi_usb_close( fc );
|
||||
ftdi_deinit( fc );
|
||||
/* mark ftdi layer as not initialized */
|
||||
p->fc = NULL;
|
||||
}
|
||||
|
||||
return r < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
usbconn_ftdi_close( usbconn_t *conn )
|
||||
{
|
||||
ftdi_param_t *p = conn->params;
|
||||
|
||||
if (p->fc)
|
||||
{
|
||||
ftdi_disable_bitbang( p->fc );
|
||||
ftdi_usb_close( p->fc );
|
||||
ftdi_deinit( p->fc );
|
||||
p->fc = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
usbconn_ftdi_free( usbconn_t *conn )
|
||||
{
|
||||
ftdi_param_t *p = conn->params;
|
||||
|
||||
if (p->send_buf)
|
||||
free( p->send_buf );
|
||||
if (p->recv_buf)
|
||||
free( p->recv_buf );
|
||||
if (p->fc)
|
||||
free( p->fc );
|
||||
if (p->serial)
|
||||
free( p->serial );
|
||||
|
||||
free( conn->params );
|
||||
free( conn );
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
usbconn_driver_t usbconn_ftdi_driver = {
|
||||
"ftdi",
|
||||
usbconn_ftdi_connect,
|
||||
usbconn_ftdi_free,
|
||||
usbconn_ftdi_open,
|
||||
usbconn_ftdi_close,
|
||||
usbconn_ftdi_read,
|
||||
usbconn_ftdi_write
|
||||
};
|
||||
|
||||
usbconn_driver_t usbconn_ftdi_mpsse_driver = {
|
||||
"ftdi-mpsse",
|
||||
usbconn_ftdi_mpsse_connect,
|
||||
usbconn_ftdi_free,
|
||||
usbconn_ftdi_mpsse_open,
|
||||
usbconn_ftdi_close,
|
||||
usbconn_ftdi_read,
|
||||
usbconn_ftdi_write
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:C
|
||||
c-default-style:gnu
|
||||
indent-tabs-mode:nil
|
||||
End:
|
||||
*/
|
Loading…
Reference in New Issue