/***************************************************************************
ftdi.c - description
-------------------
begin : Fri Apr 4 2003
copyright : (C) 2003 by Intra2net AG
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; *
* *
***************************************************************************/
#include <usb.h>
#include <string.h>
#include "ftdi.h"
#define ftdi_error_return(code, str) do { \
ftdi->error_str = str; \
return code; \
} while(0);
/* ftdi_init
Initializes a ftdi_context.
Return codes:
0: All fine
-1: Couldn't allocate read buffer
*/
int ftdi_init( struct ftdi_context *ftdi )
{
ftdi->usb_dev = NULL;
ftdi->usb_read_timeout = 5000;
ftdi->usb_write_timeout = 5000;
ftdi->type = TYPE_BM; /* chip type */
ftdi->baudrate = -1;
ftdi->bitbang_enabled = 0;
ftdi->readbuffer = NULL;
ftdi->readbuffer_offset = 0;
ftdi->readbuffer_remaining = 0;
ftdi->writebuffer_chunksize = 4096;
ftdi->interface = 0;
ftdi->index = 0;
ftdi->in_ep = 0x02;
ftdi->out_ep = 0x81;
ftdi->bitbang_mode = 1; /* 1: Normal bitbang mode, 2: SPI bitbang mode */
ftdi->error_str = NULL;
/* All fine. Now allocate the readbuffer */
return ftdi_read_data_set_chunksize( ftdi, 4096 );
}
/* ftdi_set_interface
Call after ftdi_init
Open selected channels on a chip, otherwise use first channel
0: all fine
-1: unknown interface
*/
int ftdi_set_interface( struct ftdi_context *ftdi,
enum ftdi_interface interface )
{
switch ( interface )
{
case INTERFACE_ANY:
case INTERFACE_A:
/* ftdi_usb_open_desc cares to set the right index, depending on the found chip */
break;
case INTERFACE_B:
ftdi->interface = 1;
ftdi->index = INTERFACE_B;
ftdi->in_ep = 0x04;
ftdi->out_ep = 0x83;
break;
default:
ftdi_error_return( -1, "Unknown interface" )
;
}
return 0;
}
/* ftdi_deinit
Deinitializes a ftdi_context.
*/
void ftdi_deinit( struct ftdi_context *ftdi )
{
if ( ftdi->readbuffer != NULL )
{
free( ftdi->readbuffer );
ftdi->readbuffer = NULL;
}
}
/* ftdi_set_usbdev
Use an already open device.
*/
void ftdi_set_usbdev( struct ftdi_context *ftdi, usb_dev_handle *usb )
{
ftdi->usb_dev = usb;
}
/* ftdi_usb_find_all
Finds all ftdi devices on the usb bus. Creates a new ftdi_device_list which
needs to be deallocated by ftdi_list_free after use.
Return codes:
>0: number of devices found
-1: usb_find_busses() failed
-2: usb_find_devices() failed
-3: out of memory
*/
int ftdi_usb_find_all( struct ftdi_context *ftdi,
struct ftdi_device_list **devlist, int vendor, int product )
{
struct ftdi_device_list **curdev;
struct usb_bus *bus;
struct usb_device *dev;
int count = 0;
usb_init( );
if ( usb_find_busses( ) < 0 )
ftdi_error_return( -1, "usb_find_busses() failed" );
if ( usb_find_devices( ) < 0 )
ftdi_error_return( -2, "usb_find_devices() failed" );
curdev = devlist;
for ( bus = usb_busses; bus; bus = bus->next )
{
for ( dev = bus->devices; dev; dev = dev->next )
{
if ( dev->descriptor.idVendor == vendor
&& dev->descriptor.idProduct == product )
{
*curdev = (struct ftdi_device_list*) malloc(
sizeof(struct ftdi_device_list) );
if ( ! *curdev )
ftdi_error_return( -3, "out of memory" );
( *curdev )->next = NULL;
( *curdev )->dev = dev;
curdev = &( *curdev )->next;
count++;
}
}
}
return count;
}
/* ftdi_list_free
Frees a created device list.
*/
void ftdi_list_free( struct ftdi_device_list **devlist )
{
struct ftdi_device_list **curdev;
for ( ; *devlist == NULL; devlist = curdev )
{
curdev = &( *devlist )->next;
free( *devlist );
}
devlist = NULL;
}
/* ftdi_usb_open_dev
Opens a ftdi device given by a usb_device.
Return codes:
0: all fine
-4: unable to open device
-5: unable to claim device
-6: reset failed
-7: set baudrate failed
*/
int ftdi_usb_open_dev( struct ftdi_context *ftdi, struct usb_device *dev )
{
if ( !( ftdi->usb_dev = usb_open( dev ) ) )
ftdi_error_return( -4, "usb_open() failed" );
if ( usb_claim_interface( ftdi->usb_dev, ftdi->interface ) != 0 )
{
usb_close( ftdi->usb_dev );
ftdi_error_return( -5,
"unable to claim usb device. Make sure ftdi_sio is unloaded!" );
}
if ( ftdi_usb_reset( ftdi ) != 0 )
{
usb_close( ftdi->usb_dev );
ftdi_error_return( -6, "ftdi_usb_reset failed" );
}
if ( ftdi_set_baudrate( ftdi, 9600 ) != 0 )
{
usb_close( ftdi->usb_dev );
ftdi_error_return( -7, "set baudrate failed" );
}
// Try to guess chip type
// Bug in the BM type chips: bcdDevice is 0x200 for serial == 0
if ( dev->descriptor.bcdDevice == 0x400
|| ( dev->descriptor.bcdDevice == 0x200
&& dev->descriptor.iSerialNumber == 0 ) )
ftdi->type = TYPE_BM;
else if ( dev->descriptor.bcdDevice == 0x200 )
ftdi->type = TYPE_AM;
else if ( dev->descriptor.bcdDevice == 0x500 )
{
ftdi->type = TYPE_2232C;
if ( !ftdi->index )
ftdi->index = INTERFACE_A;
}
ftdi_error_return( 0, "all fine" );
}
/* ftdi_usb_open
Opens the first device with a given vendor and product ids.
Return codes:
See ftdi_usb_open_desc()
*/
int ftdi_usb_open( struct ftdi_context *ftdi, int vendor, int product )
{
return ftdi_usb_open_desc( ftdi, vendor, product, NULL, NULL );
}
/* ftdi_usb_open_desc
Opens the first device with a given, vendor id, product id,
description and serial.
Return codes:
0: all fine
-1: usb_find_busses() failed
-2: usb_find_devices() failed
-3: usb device not found
-4: unable to open device
-5: unable to claim device
-6: reset failed
-7: set baudrate failed
-8: get product description failed
-9: get serial number failed
-10: unable to close device
*/
int ftdi_usb_open_desc( struct ftdi_context *ftdi, int vendor, int product,
const char* description, const char* serial )
{
struct usb_bus *bus;
struct usb_device *dev;
char string[ 256 ];
usb_init( );
if ( usb_find_busses( ) < 0 )
ftdi_error_return( -1, "usb_find_busses() failed" );
if ( usb_find_devices( ) < 0 )
ftdi_error_return( -2, "usb_find_devices() failed" );
for ( bus = usb_busses; bus; bus = bus->next )
{
for ( dev = bus->devices; dev; dev = dev->next )
{
if ( dev->descriptor.idVendor == vendor
&& dev->descriptor.idProduct == product )
{
if ( !( ftdi->usb_dev = usb_open( dev ) ) )
ftdi_error_return( -4, "usb_open() failed" );
if ( description != NULL )
{
if ( usb_get_string_simple( ftdi->usb_dev, dev->descriptor.iProduct,
string, sizeof( string ) ) <= 0 )
{
usb_close( ftdi->usb_dev );
ftdi_error_return( -8, "unable to fetch product description" );
}
if ( strncmp( string, description, sizeof( string ) ) != 0 )
{
if ( usb_close( ftdi->usb_dev ) != 0 )
ftdi_error_return( -10, "unable to close device" );
continue;
}
}
if ( serial != NULL )
{
if ( usb_get_string_simple( ftdi->usb_dev,
dev->descriptor.iSerialNumber, string, sizeof( string ) ) <= 0 )
{
usb_close( ftdi->usb_dev );
ftdi_error_return( -9, "unable to fetch serial number" );
}
if ( strncmp( string, serial, sizeof( string ) ) != 0 )
{
if ( usb_close( ftdi->usb_dev ) != 0 )
ftdi_error_return( -10, "unable to close device" );
continue;
}
}
if ( usb_close( ftdi->usb_dev ) != 0 )
ftdi_error_return( -10, "unable to close device" );
return ftdi_usb_open_dev( ftdi, dev );
}
}
}
// device not found
ftdi_error_return( -3, "device not found" );
}
/* ftdi_usb_reset
Resets the ftdi device.
Return codes:
0: all fine
-1: FTDI reset failed
*/
int ftdi_usb_reset( struct ftdi_context *ftdi )
{
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0, 0, ftdi->index, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "FTDI reset failed" );
// Invalidate data in the readbuffer
ftdi->readbuffer_offset = 0;
ftdi->readbuffer_remaining = 0;
return 0;
}
/* ftdi_usb_purge_buffers
Cleans the buffers of the ftdi device.
Return codes:
0: all fine
-1: write buffer purge failed
-2: read buffer purge failed
*/
int ftdi_usb_purge_buffers( struct ftdi_context *ftdi )
{
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0, 1, ftdi->index, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "FTDI purge of RX buffer failed" );
// Invalidate data in the readbuffer
ftdi->readbuffer_offset = 0;
ftdi->readbuffer_remaining = 0;
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0, 2, ftdi->index, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -2, "FTDI purge of TX buffer failed" );
return 0;
}
/* ftdi_usb_close
Closes the ftdi device.
Return codes:
0: all fine
-1: usb_release failed
-2: usb_close failed
*/
int ftdi_usb_close( struct ftdi_context *ftdi )
{
int rtn = 0;
if ( usb_release_interface( ftdi->usb_dev, ftdi->interface ) != 0 )
rtn = -1;
if ( usb_close( ftdi->usb_dev ) != 0 )
rtn = -2;
return rtn;
}
/*
ftdi_convert_baudrate returns nearest supported baud rate to that requested.
Function is only used internally
*/
static int ftdi_convert_baudrate( int baudrate, struct ftdi_context *ftdi,
unsigned short *value, unsigned short *index )
{
static const char am_adjust_up[ 8 ] =
{
0,
0,
0,
1,
0,
3,
2,
1 };
static const char am_adjust_dn[ 8 ] =
{
0,
0,
0,
1,
0,
1,
2,
3 };
static const char frac_code[ 8 ] =
{
0,
3,
2,
4,
1,
5,
6,
7 };
int divisor, best_divisor, best_baud, best_baud_diff;
unsigned long encoded_divisor;
int i;
if ( baudrate <= 0 )
{
// Return error
return -1;
}
divisor = 24000000 / baudrate;
if ( ftdi->type == TYPE_AM )
{
// Round down to supported fraction (AM only)
divisor -= am_adjust_dn[ divisor & 7 ];
}
// Try this divisor and the one above it (because division rounds down)
best_divisor = 0;
best_baud = 0;
best_baud_diff = 0;
for ( i = 0; i < 2; i++ )
{
int try_divisor = divisor + i;
int baud_estimate;
int baud_diff;
// Round up to supported divisor value
if ( try_divisor <= 8 )
{
// Round up to minimum supported divisor
try_divisor = 8;
}
else if ( ftdi->type != TYPE_AM && try_divisor < 12 )
{
// BM doesn't support divisors 9 through 11 inclusive
try_divisor = 12;
}
else if ( divisor < 16 )
{
// AM doesn't support divisors 9 through 15 inclusive
try_divisor = 16;
}
else
{
if ( ftdi->type == TYPE_AM )
{
// Round up to supported fraction (AM only)
try_divisor += am_adjust_up[ try_divisor & 7 ];
if ( try_divisor > 0x1FFF8 )
{
// Round down to maximum supported divisor value (for AM)
try_divisor = 0x1FFF8;
}
}
else
{
if ( try_divisor > 0x1FFFF )
{
// Round down to maximum supported divisor value (for BM)
try_divisor = 0x1FFFF;
}
}
}
// Get estimated baud rate (to nearest integer)
baud_estimate = ( 24000000 + ( try_divisor / 2 ) ) / try_divisor;
// Get absolute difference from requested baud rate
if ( baud_estimate < baudrate )
{
baud_diff = baudrate - baud_estimate;
}
else
{
baud_diff = baud_estimate - baudrate;
}
if ( i == 0 || baud_diff < best_baud_diff )
{
// Closest to requested baud rate so far
best_divisor = try_divisor;
best_baud = baud_estimate;
best_baud_diff = baud_diff;
if ( baud_diff == 0 )
{
// Spot on! No point trying
break;
}
}
}
// Encode the best divisor value
encoded_divisor = ( best_divisor >> 3 )
| ( frac_code[ best_divisor & 7 ] << 14 );
// Deal with special cases for encoded value
if ( encoded_divisor == 1 )
{
encoded_divisor = 0; // 3000000 baud
}
else if ( encoded_divisor == 0x4001 )
{
encoded_divisor = 1; // 2000000 baud (BM only)
}
// Split into "value" and "index" values
*value = (unsigned short) ( encoded_divisor & 0xFFFF );
if ( ftdi->type == TYPE_2232C )
{
*index = (unsigned short) ( encoded_divisor >> 8 );
*index &= 0xFF00;
*index |= ftdi->index;
}
else
*index = (unsigned short) ( encoded_divisor >> 16 );
// Return the nearest baud rate
return best_baud;
}
/*
ftdi_set_baudrate
Sets the chip baudrate
Return codes:
0: all fine
-1: invalid baudrate
-2: setting baudrate failed
*/
int ftdi_set_baudrate( struct ftdi_context *ftdi, int baudrate )
{
unsigned short value, index;
int actual_baudrate;
if ( ftdi->bitbang_enabled )
{
baudrate = baudrate * 4;
}
actual_baudrate = ftdi_convert_baudrate( baudrate, ftdi, &value, &index );
if ( actual_baudrate <= 0 )
ftdi_error_return( -1, "Silly baudrate <= 0." );
// Check within tolerance (about 5%)
if ( ( actual_baudrate * 2 < baudrate /* Catch overflows */)
|| (
( actual_baudrate < baudrate ) ?
( actual_baudrate * 21 < baudrate * 20 ) :
( baudrate * 21 < actual_baudrate * 20 ) ) )
ftdi_error_return( -1,
"Unsupported baudrate. Note: bitbang baudrates are automatically multiplied by 4" );
if ( usb_control_msg( ftdi->usb_dev, 0x40, 3, value, index, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -2, "Setting new baudrate failed" );
ftdi->baudrate = baudrate;
return 0;
}
/*
ftdi_set_line_property
set (RS232) line characteristics by Alain Abbas
Return codes:
0: all fine
-1: Setting line property failed
*/
int ftdi_set_line_property( struct ftdi_context *ftdi, enum ftdi_bits_type bits,
enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity )
{
unsigned short value = bits;
switch ( parity )
{
case NONE:
value |= ( 0x00 << 8 );
break;
case ODD:
value |= ( 0x01 << 8 );
break;
case EVEN:
value |= ( 0x02 << 8 );
break;
case MARK:
value |= ( 0x03 << 8 );
break;
case SPACE:
value |= ( 0x04 << 8 );
break;
}
switch ( sbit )
{
case STOP_BIT_1:
value |= ( 0x00 << 11 );
break;
case STOP_BIT_15:
value |= ( 0x01 << 11 );
break;
case STOP_BIT_2:
value |= ( 0x02 << 11 );
break;
}
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x04, value, ftdi->index, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "Setting new line property failed" );
return 0;
}
int ftdi_write_data( struct ftdi_context *ftdi, unsigned char *buf, int size )
{
int ret;
int offset = 0;
int total_written = 0;
while ( offset < size )
{
int write_size = ftdi->writebuffer_chunksize;
if ( offset + write_size > size )
write_size = size - offset;
ret = usb_bulk_write( ftdi->usb_dev, ftdi->in_ep, buf + offset, write_size,
ftdi->usb_write_timeout );
if ( ret < 0 )
ftdi_error_return( ret, "usb bulk write failed" );
total_written += ret;
offset += write_size;
}
return total_written;
}
int ftdi_write_data_set_chunksize( struct ftdi_context *ftdi,
unsigned int chunksize )
{
ftdi->writebuffer_chunksize = chunksize;
return 0;
}
int ftdi_write_data_get_chunksize( struct ftdi_context *ftdi,
unsigned int *chunksize )
{
*chunksize = ftdi->writebuffer_chunksize;
return 0;
}
int ftdi_read_data( struct ftdi_context *ftdi, unsigned char *buf, int size )
{
int offset = 0, ret = 1, i, num_of_chunks, chunk_remains;
// everything we want is still in the readbuffer?
if ( size <= ftdi->readbuffer_remaining )
{
memcpy( buf, ftdi->readbuffer + ftdi->readbuffer_offset, size );
// Fix offsets
ftdi->readbuffer_remaining -= size;
ftdi->readbuffer_offset += size;
/* printf("Returning bytes from buffer: %d - remaining: %d\n", size, ftdi->readbuffer_remaining); */
return size;
}
// something still in the readbuffer, but not enough to satisfy 'size'?
if ( ftdi->readbuffer_remaining != 0 )
{
memcpy( buf, ftdi->readbuffer + ftdi->readbuffer_offset,
ftdi->readbuffer_remaining );
// Fix offset
offset += ftdi->readbuffer_remaining;
}
// do the actual USB read
while ( offset < size && ret > 0 )
{
ftdi->readbuffer_remaining = 0;
ftdi->readbuffer_offset = 0;
/* returns how much received */
ret = usb_bulk_read( ftdi->usb_dev, ftdi->out_ep, ftdi->readbuffer,
ftdi->readbuffer_chunksize, ftdi->usb_read_timeout );
if ( ret < 0 )
ftdi_error_return( ret, "usb bulk read failed" );
if ( ret > 2 )
{
// skip FTDI status bytes.
// Maybe stored in the future to enable modem use
num_of_chunks = ret / 64;
chunk_remains = ret % 64;
//printf("ret = %X, num_of_chunks = %X, chunk_remains = %X, readbuffer_offset = %X\n", ret, num_of_chunks, chunk_remains, ftdi->readbuffer_offset);
ftdi->readbuffer_offset += 2;
ret -= 2;
if ( ret > 62 )
{
for ( i = 1; i < num_of_chunks; i++ )
memmove( ftdi->readbuffer + ftdi->readbuffer_offset + 62 * i,
ftdi->readbuffer + ftdi->readbuffer_offset + 64 * i, 62 );
if ( chunk_remains > 2 )
{
memmove( ftdi->readbuffer + ftdi->readbuffer_offset + 62 * i,
ftdi->readbuffer + ftdi->readbuffer_offset + 64 * i,
chunk_remains - 2 );
ret -= 2 * num_of_chunks;
}
else
ret -= 2 * ( num_of_chunks - 1 ) + chunk_remains;
}
}
else if ( ret <= 2 )
{
// no more data to read?
return offset;
}
if ( ret > 0 )
{
// data still fits in buf?
if ( offset + ret <= size )
{
memcpy( buf + offset, ftdi->readbuffer + ftdi->readbuffer_offset, ret );
//printf("buf[0] = %X, buf[1] = %X\n", buf[0], buf[1]);
offset += ret;
/* Did we read exactly the right amount of bytes? */
if ( offset == size )
//printf("read_data exact rem %d offset %d\n",
//ftdi->readbuffer_remaining, offset);
return offset;
}
else
{
// only copy part of the data or size <= readbuffer_chunksize
int part_size = size - offset;
memcpy( buf + offset, ftdi->readbuffer + ftdi->readbuffer_offset,
part_size );
ftdi->readbuffer_offset += part_size;
ftdi->readbuffer_remaining = ret - part_size;
offset += part_size;
/* printf("Returning part: %d - size: %d - offset: %d - ret: %d - remaining: %d\n",
part_size, size, offset, ret, ftdi->readbuffer_remaining); */
return offset;
}
}
}
// never reached
return -127;
}
int ftdi_read_data_set_chunksize( struct ftdi_context *ftdi,
unsigned int chunksize )
{
unsigned char *new_buf;
// Invalidate all remaining data
ftdi->readbuffer_offset = 0;
ftdi->readbuffer_remaining = 0;
if ( ( new_buf = (unsigned char *) realloc( ftdi->readbuffer, chunksize ) )
== NULL )
ftdi_error_return( -1, "out of memory for readbuffer" );
ftdi->readbuffer = new_buf;
ftdi->readbuffer_chunksize = chunksize;
return 0;
}
int ftdi_read_data_get_chunksize( struct ftdi_context *ftdi,
unsigned int *chunksize )
{
*chunksize = ftdi->readbuffer_chunksize;
return 0;
}
int ftdi_enable_bitbang( struct ftdi_context *ftdi, unsigned char bitmask )
{
unsigned short usb_val;
usb_val = bitmask; // low byte: bitmask
/* FT2232C: Set bitbang_mode to 2 to enable SPI */
usb_val |= ( ftdi->bitbang_mode << 8 );
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x0B, usb_val, ftdi->index, NULL,
0, ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1,
"unable to enter bitbang mode. Perhaps not a BM type chip?" );
ftdi->bitbang_enabled = 1;
return 0;
}
int ftdi_disable_bitbang( struct ftdi_context *ftdi )
{
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x0B, 0, ftdi->index, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1,
"unable to leave bitbang mode. Perhaps not a BM type chip?" );
ftdi->bitbang_enabled = 0;
return 0;
}
int ftdi_set_bitmode( struct ftdi_context *ftdi, unsigned char bitmask,
unsigned char mode )
{
unsigned short usb_val;
usb_val = bitmask; // low byte: bitmask
usb_val |= ( mode << 8 );
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x0B, usb_val, ftdi->index, NULL,
0, ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1,
"unable to configure bitbang mode. Perhaps not a 2232C type chip?" );
ftdi->bitbang_mode = mode;
ftdi->bitbang_enabled =
( mode == BITMODE_BITBANG || mode == BITMODE_SYNCBB ) ? 1 : 0;
return 0;
}
int ftdi_read_pins( struct ftdi_context *ftdi, unsigned char *pins )
{
unsigned short usb_val;
if ( usb_control_msg( ftdi->usb_dev, 0xC0, 0x0C, 0, ftdi->index,
(char *) &usb_val, 1, ftdi->usb_read_timeout ) != 1 )
ftdi_error_return( -1, "read pins failed" );
*pins = (unsigned char) usb_val;
return 0;
}
int ftdi_set_latency_timer( struct ftdi_context *ftdi, unsigned char latency )
{
unsigned short usb_val;
if ( latency < 1 )
ftdi_error_return( -1, "latency out of range. Only valid for 1-255" );
usb_val = latency;
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x09, usb_val, ftdi->index, NULL,
0, ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -2, "unable to set latency timer" );
return 0;
}
int ftdi_get_latency_timer( struct ftdi_context *ftdi, unsigned char *latency )
{
unsigned short usb_val;
if ( usb_control_msg( ftdi->usb_dev, 0xC0, 0x0A, 0, ftdi->index,
(char *) &usb_val, 1, ftdi->usb_read_timeout ) != 1 )
ftdi_error_return( -1, "reading latency timer failed" );
*latency = (unsigned char) usb_val;
return 0;
}
void ftdi_eeprom_initdefaults( struct ftdi_eeprom *eeprom )
{
eeprom->vendor_id = 0x0403;
eeprom->product_id = 0x6001;
eeprom->self_powered = 1;
eeprom->remote_wakeup = 1;
eeprom->BM_type_chip = 1;
eeprom->in_is_isochronous = 0;
eeprom->out_is_isochronous = 0;
eeprom->suspend_pull_downs = 0;
eeprom->use_serial = 0;
eeprom->change_usb_version = 0;
eeprom->usb_version = 0x0200;
eeprom->max_power = 0;
eeprom->manufacturer = NULL;
eeprom->product = NULL;
eeprom->serial = NULL;
}
/*
ftdi_eeprom_build
Build binary output from ftdi_eeprom structure.
Output is suitable for ftdi_write_eeprom.
Return codes:
positive value: used eeprom size
-1: eeprom size (128 bytes) exceeded by custom strings
*/
int ftdi_eeprom_build( struct ftdi_eeprom *eeprom, unsigned char *output )
{
unsigned char i, j;
unsigned short checksum, value;
unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
int size_check;
if ( eeprom->manufacturer != NULL )
manufacturer_size = strlen( eeprom->manufacturer );
if ( eeprom->product != NULL )
product_size = strlen( eeprom->product );
if ( eeprom->serial != NULL )
serial_size = strlen( eeprom->serial );
size_check = 128; // eeprom is 128 bytes
size_check -= 28; // 28 are always in use (fixed)
size_check -= manufacturer_size * 2;
size_check -= product_size * 2;
size_check -= serial_size * 2;
// eeprom size exceeded?
if ( size_check < 0 )
return ( -1 );
// empty eeprom
memset( output, 0, 128 );
// Addr 00: Stay 00 00
// Addr 02: Vendor ID
output[ 0x02 ] = eeprom->vendor_id;
output[ 0x03 ] = eeprom->vendor_id >> 8;
// Addr 04: Product ID
output[ 0x04 ] = eeprom->product_id;
output[ 0x05 ] = eeprom->product_id >> 8;
// Addr 06: Device release number (0400h for BM features)
output[ 0x06 ] = 0x00;
if ( eeprom->BM_type_chip == 1 )
output[ 0x07 ] = 0x04;
else
output[ 0x07 ] = 0x02;
// Addr 08: Config descriptor
// Bit 1: remote wakeup if 1
// Bit 0: self powered if 1
//
j = 0;
if ( eeprom->self_powered == 1 )
j = j | 1;
if ( eeprom->remote_wakeup == 1 )
j = j | 2;
output[ 0x08 ] = j;
// Addr 09: Max power consumption: max power = value * 2 mA
output[ 0x09 ] = eeprom->max_power;
;
// Addr 0A: Chip configuration
// Bit 7: 0 - reserved
// Bit 6: 0 - reserved
// Bit 5: 0 - reserved
// Bit 4: 1 - Change USB version
// Bit 3: 1 - Use the serial number string
// Bit 2: 1 - Enable suspend pull downs for lower power
// Bit 1: 1 - Out EndPoint is Isochronous
// Bit 0: 1 - In EndPoint is Isochronous
//
j = 0;
if ( eeprom->in_is_isochronous == 1 )
j = j | 1;
if ( eeprom->out_is_isochronous == 1 )
j = j | 2;
if ( eeprom->suspend_pull_downs == 1 )
j = j | 4;
if ( eeprom->use_serial == 1 )
j = j | 8;
if ( eeprom->change_usb_version == 1 )
j = j | 16;
output[ 0x0A ] = j;
// Addr 0B: reserved
output[ 0x0B ] = 0x00;
// Addr 0C: USB version low byte when 0x0A bit 4 is set
// Addr 0D: USB version high byte when 0x0A bit 4 is set
if ( eeprom->change_usb_version == 1 )
{
output[ 0x0C ] = eeprom->usb_version;
output[ 0x0D ] = eeprom->usb_version >> 8;
}
// Addr 0E: Offset of the manufacturer string + 0x80
output[ 0x0E ] = 0x14 + 0x80;
// Addr 0F: Length of manufacturer string
output[ 0x0F ] = manufacturer_size * 2 + 2;
// Addr 10: Offset of the product string + 0x80, calculated later
// Addr 11: Length of product string
output[ 0x11 ] = product_size * 2 + 2;
// Addr 12: Offset of the serial string + 0x80, calculated later
// Addr 13: Length of serial string
output[ 0x13 ] = serial_size * 2 + 2;
// Dynamic content
output[ 0x14 ] = manufacturer_size * 2 + 2;
output[ 0x15 ] = 0x03; // type: string
i = 0x16, j = 0;
// Output manufacturer
for ( j = 0; j < manufacturer_size; j++ )
{
output[ i ] = eeprom->manufacturer[ j ], i++;
output[ i ] = 0x00, i++;
}
// Output product name
output[ 0x10 ] = i + 0x80; // calculate offset
output[ i ] = product_size * 2 + 2, i++;
output[ i ] = 0x03, i++;
for ( j = 0; j < product_size; j++ )
{
output[ i ] = eeprom->product[ j ], i++;
output[ i ] = 0x00, i++;
}
// Output serial
output[ 0x12 ] = i + 0x80; // calculate offset
output[ i ] = serial_size * 2 + 2, i++;
output[ i ] = 0x03, i++;
for ( j = 0; j < serial_size; j++ )
{
output[ i ] = eeprom->serial[ j ], i++;
output[ i ] = 0x00, i++;
}
// calculate checksum
checksum = 0xAAAA;
for ( i = 0; i < 63; i++ )
{
value = output[ i * 2 ];
value += output[ ( i * 2 ) + 1 ] << 8;
checksum = value ^ checksum;
checksum = ( checksum << 1 ) | ( checksum >> 15 );
}
output[ 0x7E ] = checksum;
output[ 0x7F ] = checksum >> 8;
return size_check;
}
int ftdi_read_eeprom( struct ftdi_context *ftdi, unsigned char *eeprom )
{
int i;
for ( i = 0; i < 64; i++ )
{
if ( usb_control_msg( ftdi->usb_dev, 0xC0, 0x90, 0, i, eeprom + ( i * 2 ),
2, ftdi->usb_read_timeout ) != 2 )
ftdi_error_return( -1, "reading eeprom failed" );
}
return 0;
}
int ftdi_write_eeprom( struct ftdi_context *ftdi, unsigned char *eeprom )
{
unsigned short usb_val;
int i;
for ( i = 0; i < 64; i++ )
{
usb_val = eeprom[ i * 2 ];
usb_val += eeprom[ ( i * 2 ) + 1 ] << 8;
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x91, usb_val, i, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "unable to write eeprom" );
}
return 0;
}
int ftdi_erase_eeprom( struct ftdi_context *ftdi )
{
if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x92, 0, 0, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "unable to erase eeprom" );
return 0;
}
char *ftdi_get_error_string( struct ftdi_context *ftdi )
{
return ftdi->error_str;
}
int ftdi_setflowctrl( struct ftdi_context *ftdi, int flowctrl )
{
if ( usb_control_msg( ftdi->usb_dev, SIO_SET_FLOW_CTRL_REQUEST_TYPE,
SIO_SET_FLOW_CTRL_REQUEST, 0, ( flowctrl | ftdi->interface ), NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "set flow control failed" );
return 0;
}
int ftdi_setdtr( struct ftdi_context *ftdi, int state )
{
unsigned short usb_val;
if ( state )
usb_val = SIO_SET_DTR_HIGH;
else
usb_val = SIO_SET_DTR_LOW;
if ( usb_control_msg( ftdi->usb_dev, SIO_SET_MODEM_CTRL_REQUEST_TYPE,
SIO_SET_MODEM_CTRL_REQUEST, usb_val, ftdi->interface, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "set dtr failed" );
return 0;
}
int ftdi_setrts( struct ftdi_context *ftdi, int state )
{
unsigned short usb_val;
if ( state )
usb_val = SIO_SET_RTS_HIGH;
else
usb_val = SIO_SET_RTS_LOW;
if ( usb_control_msg( ftdi->usb_dev, SIO_SET_MODEM_CTRL_REQUEST_TYPE,
SIO_SET_MODEM_CTRL_REQUEST, usb_val, ftdi->interface, NULL, 0,
ftdi->usb_write_timeout ) != 0 )
ftdi_error_return( -1, "set of rts failed" );
return 0;
}