From: Thomas Jarosch Date: Fri, 20 May 2011 14:10:11 +0000 (+0200) Subject: Merge branch 'eeprom-new' X-Git-Tag: v1.0rc1~133 X-Git-Url: http://developer.intra2net.com/git/?p=libftdi;a=commitdiff_plain;h=f276fe667f966ba9e39ec67c64443e91aec479f0;hp=ce3940810901906eb00d67fe259d5540a9008630 Merge branch 'eeprom-new' Conflicts: examples/stream_test.c --- diff --git a/.gitignore b/.gitignore index 4bacdb7..5a47eba 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.pc .deps/ .libs/ +.kdev4/ build/ # Automake diff --git a/CMakeLists.txt b/CMakeLists.txt index a661d0f..73517cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ endif(${UNIX}) add_subdirectory(src) add_subdirectory(ftdipp) add_subdirectory(bindings) +add_subdirectory(ftdi_eeprom) add_subdirectory(examples) add_subdirectory(packages) diff --git a/bindings/ftdi.i b/bindings/ftdi.i index ce1149c..b1d550a 100644 --- a/bindings/ftdi.i +++ b/bindings/ftdi.i @@ -41,12 +41,12 @@ extern "C" { %clear unsigned short *status; %apply char *OUTPUT { unsigned char *output }; - int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output); + int ftdi_eeprom_build(struct ftdi_context *ftdi); %clear unsigned char *output; %apply char *OUTPUT { unsigned char *eeprom }; - int ftdi_read_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom); - int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom); + int ftdi_read_eeprom(struct ftdi_context *ftdi); + int ftdi_write_eeprom(struct ftdi_context *ftdi); %clear unsigned char *eeprom; %apply int *OUTPUT { unsigned int *chipid }; diff --git a/doc/EEPROM-structure b/doc/EEPROM-structure new file mode 100644 index 0000000..c83337e --- /dev/null +++ b/doc/EEPROM-structure @@ -0,0 +1,105 @@ +Here we try to document what we know about the EEPROM Structure. + +Even with a 93xx66 EEPROM, at maximum 256 Bytes are used + +All important things happen in the first +0x14(FT232/245), 0x16(FT2232CD), 0x18(FT232/245R) or 0x1a (FT2232H/4432H) bytes + +Type | Use extra EEPROM space +FT2XXB | No + +Byte.BIT| TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H +00.0 | 0 0 channel_a_type channel_a_type channel_a_type +00.1 | 0 0 channel_a_type channel_a_type channel_a_type +00.2 | 0 0 channel_a_type high_current channel_a_type channel_a_type +00.3 | 0 0 channel_a_driver channel_a_driver channel_a_driver channel_a_driver +00.4 | 0 0 high_current_a 0 0 0 +00.5 | 0 0 0 0 0 0 +00.6 | 0 0 0 0 0 0 +00.7 | 0 0 0 0 SUSPEND_DBUS7 0 + +On TYPE_R 00.3 set mean D2XX, on other devices VCP + +01.0 | 0 0 channel_b_type channel_b_type channel_b_type +01.1 | 0 0 channel_b_type channel_b_type channel_b_type +01.2 | 0 0 channel_b_type 0 channel_b_type channel_b_type +01.3 | 0 0 channel_b_driver 0 channel_b_driver channel_b_driver +01.4 | 0 0 high_current_b 0 0 0 +01.5 | 0 0 0 0 0 0 +01.6 | 0 0 0 0 0 +01.7 | 0 0 0 0 0 0 + +Fixme: Missing 4232H validation and channel_c_driver, channel_d_driver, channel_a|b|c|d_rs484enable + +02 | Vendor ID (VID) LSB (all) +03 | Vendor ID (VID) MSB (all) +04 | Product ID (PID) LSB (all) +05 | Product ID (PID) MSB (all) +06 | Device release number LSB (not tested on TYPE_4232H) +07 | Device release number MSB (not tested on TYPE_4232H) + | +08.4 | Battery powered +08.5 | Remote wakeup +08.6 | Self powered: 1, bus powered: 0 +08.7 | Always 1 + | +09 | Max power (mA/2) + | +Byte.BIT| TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H +0a.0 | 0 IsoIn IsoIn part A 0 0 0 +0a.1 | 0 IsoOut IsoOut part A 0 0 0 +0a.2 | 0 suspend_pull_down suspend_pull_down suspend_pull_down +0a.3 | 0 use_serial use_serial +0a.4 | 0 change_usb_version change_usb_version +0a.5 | 0 0 IsoIn part B 0 0 0 +0a.6 | 0 0 IsoOut part B 0 0 0 +0a.7 | 0 - reserved + + | +0b | TYPE_R Bitmask Invert, 0 else + +Byte | TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H +0c | 0 USB-VER-LSB USB-VER-LSB 0 ? ? +0d | 0 USB-VER-MSB USB-VER-MSB 0 ? ? +(On several FT2232H different values were observed -> The value is unused + if change USB version is not set, so it might contain garbage) + +0e | OFFSET Vendor +0f | Len VENDOR + +10 | Offset Product +11 | Length Product + +12 | Offset Serial +13 | Length Serial + +Byte.BIT| TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H +14.3:0 | UA UA CHIP CBUS[0] AL A +14.7:0 | UA UA CHIP CBUS[1] AH B +15.3:0 | UA UA 0 CBUS[2] BL C +15.7:0 | UA UA 0 CBUS[3] BH D +16.3:0 | UA UA UA CBUS[4] 0 0 +16.7:0 | UA UA UA 0 0 0 + +CHIP values: +0x46: EEPROM is a 93xx46 +0x56: EEPROM is a 93xx56 +0x66: EEPROM is a 93xx66 + +17 UA UA UA 0 0 0 +18 UA UA UA VENDOR CHIP CHIP +19 UA UA UA VENDOR 0 0 + +1a UA (all) + + +Additional fields after the serial string: +0x00, 0x00 - reserved for "legacy port name prefix" +0x00, 0x00 - reserved for plug and play options +(Observed values with PnP == 0: +0x02 0x03 0x01 0x00) + +Note: The additional fields after the serial number string +collide with the official FTDI formula from AN_121 regarding +the start of the user area: +"Start Address = the address following the last byte of SerialNumber string." diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index af7f57a..d8be6f5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -21,6 +21,7 @@ if (EXAMPLES) add_executable(serial_read serial_read.c) add_executable(baud_test baud_test.c) add_executable(stream_test stream_test.c) + add_executable(eeprom eeprom.c) # Linkage target_link_libraries(simple ftdi) @@ -32,6 +33,7 @@ if (EXAMPLES) target_link_libraries(serial_read ftdi) target_link_libraries(baud_test ftdi) target_link_libraries(stream_test ftdi) + target_link_libraries(eeprom ftdi) # libftdi++ examples if(FTDI_BUILD_CPP) diff --git a/examples/baud_test.c b/examples/baud_test.c index b53031b..d8d4886 100644 --- a/examples/baud_test.c +++ b/examples/baud_test.c @@ -12,7 +12,7 @@ * s::: first device with given vendor id, product id and serial string * -d * -b (divides by 16 if bitbang as taken from the ftdi datasheets) - * -m r: serial a: async bitbang s:sync bitbang + * -m r: serial a: async bitbang s:sync bitbang * -c * * (C) 2009 by Gerd v. Egidy @@ -39,7 +39,7 @@ double get_prec_time() { struct timeval tv; double res; - + gettimeofday(&tv,NULL); res=tv.tv_sec; @@ -65,7 +65,7 @@ int main(int argc, char **argv) char *devicedesc=default_devicedesc; int txchunksize=256; enum ftdi_mpsse_mode test_mode=BITMODE_BITBANG; - + while ((t = getopt (argc, argv, "b:d:p:m:c:")) != -1) { switch (t) @@ -74,7 +74,7 @@ int main(int argc, char **argv) datasize = atoi (optarg); break; case 'm': - switch(*optarg) + switch (*optarg) { case 'r': // serial @@ -162,16 +162,16 @@ int main(int argc, char **argv) } if (ftdi_write_data_set_chunksize(&ftdic, txchunksize) < 0 || - ftdi_read_data_set_chunksize(&ftdic, txchunksize) < 0) + ftdi_read_data_set_chunksize(&ftdic, txchunksize) < 0) { fprintf(stderr,"Can't set chunksize: %s\n",ftdi_get_error_string(&ftdic)); return EXIT_FAILURE; } - if(test_mode==BITMODE_SYNCBB) + if (test_mode==BITMODE_SYNCBB) { // completely clear the receive buffer before beginning - while(ftdi_read_data(&ftdic, rxbuf, txchunksize)>0); + while (ftdi_read_data(&ftdic, rxbuf, txchunksize)>0); } start=get_prec_time(); @@ -181,7 +181,7 @@ int main(int argc, char **argv) ftdic.usb_read_timeout=1; i=0; - while(i < datasize) + while (i < datasize) { int sendsize=txchunksize; if (i+sendsize > datasize) @@ -196,7 +196,7 @@ int main(int argc, char **argv) i+=sendsize; - if(test_mode==BITMODE_SYNCBB) + if (test_mode==BITMODE_SYNCBB) { // read the same amount of data as sent ftdi_read_data(&ftdic, rxbuf, sendsize); diff --git a/examples/eeprom.c b/examples/eeprom.c new file mode 100644 index 0000000..e2c8052 --- /dev/null +++ b/examples/eeprom.c @@ -0,0 +1,236 @@ +/* LIBFTDI EEPROM access example + + This program is distributed under the GPL, version 2 +*/ + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + struct ftdi_context *ftdi; + unsigned char buf[256]; + int f, i, j; + int vid = 0x0403; + int pid = 0x6010; + char const *desc = 0; + char const *serial = 0; + int erase = 0; + int use_defaults = 0; + int large_chip = 0; + int do_write = 0; + int size; + int value; + + if ((ftdi = ftdi_new()) == 0) + { + fprintf(stderr, "Failed to allocate ftdi structure :%s \n", + ftdi_get_error_string(ftdi)); + return EXIT_FAILURE; + } + + while ((i = getopt(argc, argv, "d::ev:p:l:P:S:w")) != -1) + { + switch (i) + { + case 'd': + use_defaults = 1; + if (optarg) + large_chip = 0x66; + break; + case 'e': + erase = 1; + break; + case 'v': + vid = strtoul(optarg, NULL, 0); + break; + case 'p': + pid = strtoul(optarg, NULL, 0); + break; + case 'P': + desc = optarg; + break; + case 'S': + serial = optarg; + break; + case 'w': + do_write = 1; + break; + default: + fprintf(stderr, "usage: %s [options]\n", *argv); + fprintf(stderr, "\t-d[num] Work with default valuesfor 128 Byte " + "EEPROM or for 256 Byte EEPROM if some [num] is given\n"); + fprintf(stderr, "\t-w write\n"); + fprintf(stderr, "\t-e erase\n"); + fprintf(stderr, "\t-v verbose decoding\n"); + fprintf(stderr, "\t-p Search for device with PID == number\n"); + fprintf(stderr, "\t-v Search for device with VID == number\n"); + fprintf(stderr, "\t-P type, value); + if (ftdi->type == TYPE_R) + size = 0xa0; + else + size = value; + ftdi_get_eeprom_buf(ftdi, buf, size); + for (i=0; i < size; i += 16) + { + fprintf(stdout,"0x%03x:", i); + + for (j = 0; j< 8; j++) + fprintf(stdout," %02x", buf[i+j]); + fprintf(stdout," "); + for (; j< 16; j++) + fprintf(stdout," %02x", buf[i+j]); + fprintf(stdout," "); + for (j = 0; j< 8; j++) + fprintf(stdout,"%c", isprint(buf[i+j])?buf[i+j]:'.'); + fprintf(stdout," "); + for (; j< 16; j++) + fprintf(stdout,"%c", isprint(buf[i+j])?buf[i+j]:'.'); + fprintf(stdout,"\n"); + } + + f = ftdi_eeprom_decode(ftdi, 1); + if (f < 0) + { + fprintf(stderr, "ftdi_eeprom_decode: %d (%s)\n", + f, ftdi_get_error_string(ftdi)); + exit(-1); + } + + ftdi_usb_close(ftdi); + ftdi_free(ftdi); + return 0; +} diff --git a/examples/serial_read.c b/examples/serial_read.c index 5d05d66..aec18d0 100644 --- a/examples/serial_read.c +++ b/examples/serial_read.c @@ -25,21 +25,21 @@ int main(int argc, char **argv) { switch (i) { - case 'i': // 0=ANY, 1=A, 2=B, 3=C, 4=D - interface = strtoul(optarg, NULL, 0); - break; - case 'v': - vid = strtoul(optarg, NULL, 0); - break; - case 'p': - pid = strtoul(optarg, NULL, 0); - break; - case 'b': - baudrate = strtoul(optarg, NULL, 0); - break; - default: - fprintf(stderr, "usage: %s [-i interface] [-v vid] [-p pid] [-b baudrate]\n", *argv); - exit(-1); + case 'i': // 0=ANY, 1=A, 2=B, 3=C, 4=D + interface = strtoul(optarg, NULL, 0); + break; + case 'v': + vid = strtoul(optarg, NULL, 0); + break; + case 'p': + pid = strtoul(optarg, NULL, 0); + break; + case 'b': + baudrate = strtoul(optarg, NULL, 0); + break; + default: + fprintf(stderr, "usage: %s [-i interface] [-v vid] [-p pid] [-b baudrate]\n", *argv); + exit(-1); } } @@ -70,11 +70,12 @@ int main(int argc, char **argv) } // Read data forever - while ((f = ftdi_read_data(&ftdic, buf, sizeof(buf))) >= 0) { - fprintf(stderr, "read %d bytes\n", f); - fwrite(buf, f, 1, stdout); - fflush(stderr); - fflush(stdout); + while ((f = ftdi_read_data(&ftdic, buf, sizeof(buf))) >= 0) + { + fprintf(stderr, "read %d bytes\n", f); + fwrite(buf, f, 1, stdout); + fflush(stderr); + fflush(stdout); } ftdi_usb_close(&ftdic); diff --git a/ftdi_eeprom/CMakeLists.txt b/ftdi_eeprom/CMakeLists.txt new file mode 100644 index 0000000..3f1d7dc --- /dev/null +++ b/ftdi_eeprom/CMakeLists.txt @@ -0,0 +1,41 @@ +set(FTDI_BUILD_EEPROM False PARENT_SCOPE) + +option(FTDI_EEPROM "Build ftdi_eeprom" ON) + +if (FTDI_EEPROM) + include(FindPkgConfig) + pkg_check_modules(Confuse libconfuse) + INCLUDE_DIRECTORIES(${Confuse_INCLUDE_DIRS}) + LINK_DIRECTORIES(${Confuse_LIBRARY_DIRS}) + SET(libs ${libs} ${Confuse_LIBRARIES}) + + if(Confuse_FOUND) + set(FTDI_BUILD_EEPROM True PARENT_SCOPE) + message(STATUS "Building ftdi_eeprom") + + # Version defines + set(EEPROM_MAJOR_VERSION 0) + set(EEPROM_MINOR_VERSION 17) + set(EEPROM_VERSION_STRING ${EEPROM_MAJOR_VERSION}.${EEPROM_MINOR_VERSION}) + + include_directories(${CMAKE_SOURCE_DIR}/src) + include_directories(${CMAKE_BINARY_DIR}/ftdi_eeprom) + + configure_file( + "ftdi_eeprom_version.h.in" + "${CMAKE_BINARY_DIR}/ftdi_eeprom/ftdi_eeprom_version.h" + ) + + add_executable(ftdi_eeprom main.c) + target_link_libraries(ftdi_eeprom ftdi) + target_link_libraries(ftdi_eeprom ${Confuse_LIBRARIES}) + + else(Confuse_FOUND) + message(STATUS "libConfuse not found, won't build ftdi_eeprom") + endif(Confuse_FOUND) + +else(FTDI_EEPROM) + + message(STATUS "ftdi_eeprom build is disabled") + +endif(FTDI_EEPROM) diff --git a/ftdi_eeprom/example.conf b/ftdi_eeprom/example.conf new file mode 100644 index 0000000..cfc9fc3 --- /dev/null +++ b/ftdi_eeprom/example.conf @@ -0,0 +1,32 @@ +vendor_id=0x0403 # Vendor ID +product_id=0x6001 # Product ID + +max_power=0 # Max. power consumption: value * 2 mA. Use 0 if self_powered = true. + +########### +# Strings # +########### +manufacturer="ACME Inc" # Manufacturer +product="USB Serial Converter" # Product +serial="08-15" # Serial + +########### +# Options # +########### +self_powered=true # Turn this off for bus powered +remote_wakeup=false # Turn this on for remote wakeup feature +use_serial=true # Use the serial number string + +# Normally out don't have to change one of these flags +BM_type_chip=true # Newer chips are all BM type +in_is_isochronous=false # In Endpoint is Isochronous +out_is_isochronous=false # Out Endpoint is Isochronous +suspend_pull_downs=false # Enable suspend pull downs for lower power +change_usb_version=false # Change USB Version +usb_version=0x0200 # Only used when change_usb_version is enabled + +######## +# Misc # +######## + +filename="eeprom.new" # Filename, leave empty to skip file writing diff --git a/ftdi_eeprom/ftdi_eeprom_version.h.in b/ftdi_eeprom/ftdi_eeprom_version.h.in new file mode 100644 index 0000000..db7717d --- /dev/null +++ b/ftdi_eeprom/ftdi_eeprom_version.h.in @@ -0,0 +1,8 @@ +#ifndef _FTDI_EEPROM_VERSION_H +#define _FTDI_EEPROM_VERSION_H + +#define EEPROM_MAJOR_VERSION @EEPROM_MAJOR_VERSION@ +#define EEPROM_MINOR_VERSION @EEPROM_MINOR_VERSION@ +#define EEPROM_VERSION_STRING "@EEPROM_VERSION_STRING@" + +#endif diff --git a/ftdi_eeprom/main.c b/ftdi_eeprom/main.c new file mode 100644 index 0000000..3977f86 --- /dev/null +++ b/ftdi_eeprom/main.c @@ -0,0 +1,394 @@ +/*************************************************************************** + main.c - description + ------------------- + begin : Mon Apr 7 12:05:22 CEST 2003 + copyright : (C) 2003-2011 by Intra2net AG and the libftdi developers + email : opensource@intra2net.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 as * + * published by the Free Software Foundation. * + * * + ***************************************************************************/ + +/* + TODO: + - Remove 128 bytes limit + - Merge Uwe's eeprom tool. Current features: + - Init eeprom defaults based upon eeprom type + - Read -> Already there + - Write -> Already there + - Erase -> Already there + - Decode on stdout + - Ability to find device by PID/VID, product name or serial + + TODO nice-to-have: + - Out-of-the-box compatibility with FTDI's eeprom tool configuration files + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include + +static int str_to_cbus(char *str, int max_allowed) +{ + #define MAX_OPTION 14 + const char* options[MAX_OPTION] = { + "TXDEN", "PWREN", "RXLED", "TXLED", "TXRXLED", "SLEEP", + "CLK48", "CLK24", "CLK12", "CLK6", + "IO_MODE", "BITBANG_WR", "BITBANG_RD", "SPECIAL"}; + int i; + max_allowed += 1; + if (max_allowed > MAX_OPTION) max_allowed = MAX_OPTION; + for (i=0; i\n"); + + if (argc != 2 && argc != 3) + { + printf("Syntax: %s [commands] config-file\n", argv[0]); + printf("Valid commands:\n"); + printf("--read-eeprom Read eeprom and write to -filename- from config-file\n"); + printf("--erase-eeprom Erase eeprom\n"); + printf("--flash-eeprom Flash eeprom\n"); + exit (-1); + } + + if (argc == 3) + { + if (strcmp(argv[1], "--read-eeprom") == 0) + _read = 1; + if (strcmp(argv[1], "--erase-eeprom") == 0) + _erase = 1; + if (strcmp(argv[1], "--flash-eeprom") == 0) + _flash = 1; + + argc_filename = 2; + } + else + { + argc_filename = 1; + } + + if ((fp = fopen(argv[argc_filename], "r")) == NULL) + { + printf ("Can't open configuration file\n"); + exit (-1); + } + fclose (fp); + + cfg = cfg_init(opts, 0); + cfg_parse(cfg, argv[argc_filename]); + filename = cfg_getstr(cfg, "filename"); + + if (cfg_getbool(cfg, "self_powered") && cfg_getint(cfg, "max_power") > 0) + printf("Hint: Self powered devices should have a max_power setting of 0.\n"); + + if ((ftdi = ftdi_new()) == 0) + { + fprintf(stderr, "Failed to allocate ftdi structure :%s \n", + ftdi_get_error_string(ftdi)); + return EXIT_FAILURE; + } + + ftdi_eeprom_initdefaults (ftdi, "Acme Inc.", "FTDI Chip", NULL); + + eeprom_set_value(ftdi, VENDOR_ID, cfg_getint(cfg, "vendor_id")); + eeprom_set_value(ftdi, PRODUCT_ID, cfg_getint(cfg, "product_id")); + + // TODO: Support all chip types + char *type = cfg_getstr(cfg, "chip_type"); + if (!strcmp(type, "BM")) { + ftdi->type = TYPE_BM; + } else if (!strcmp(type, "R")) { + ftdi->type = TYPE_R; + } else { + ftdi->type = TYPE_AM; + } + + eeprom_set_value(ftdi, SELF_POWERED, cfg_getbool(cfg, "self_powered")); + eeprom_set_value(ftdi, REMOTE_WAKEUP, cfg_getbool(cfg, "remote_wakeup")); + eeprom_set_value(ftdi, MAX_POWER, cfg_getint(cfg, "max_power")); + + eeprom_set_value(ftdi, IN_IS_ISOCHRONOUS, cfg_getbool(cfg, "in_is_isochronous")); + eeprom_set_value(ftdi, OUT_IS_ISOCHRONOUS, cfg_getbool(cfg, "out_is_isochronous")); + eeprom_set_value(ftdi, SUSPEND_PULL_DOWNS, cfg_getbool(cfg, "suspend_pull_downs")); + + eeprom_set_value(ftdi, USE_SERIAL, cfg_getbool(cfg, "use_serial")); + eeprom_set_value(ftdi, USE_USB_VERSION, cfg_getbool(cfg, "change_usb_version")); + eeprom_set_value(ftdi, USB_VERSION, cfg_getint(cfg, "usb_version")); + + + ftdi->eeprom->manufacturer = cfg_getstr(cfg, "manufacturer"); + ftdi->eeprom->product = cfg_getstr(cfg, "product"); + ftdi->eeprom->serial = cfg_getstr(cfg, "serial"); + eeprom_set_value(ftdi, HIGH_CURRENT, cfg_getbool(cfg, "high_current")); + eeprom_set_value(ftdi, CBUS_FUNCTION_0, str_to_cbus(cfg_getstr(cfg, "cbus0"), 13)); + eeprom_set_value(ftdi, CBUS_FUNCTION_1, str_to_cbus(cfg_getstr(cfg, "cbus1"), 13)); + eeprom_set_value(ftdi, CBUS_FUNCTION_2, str_to_cbus(cfg_getstr(cfg, "cbus2"), 13)); + eeprom_set_value(ftdi, CBUS_FUNCTION_3, str_to_cbus(cfg_getstr(cfg, "cbus3"), 13)); + eeprom_set_value(ftdi, CBUS_FUNCTION_4, str_to_cbus(cfg_getstr(cfg, "cbus4"), 9)); + int invert = 0; + if (cfg_getbool(cfg, "invert_rxd")) invert |= INVERT_RXD; + if (cfg_getbool(cfg, "invert_txd")) invert |= INVERT_TXD; + if (cfg_getbool(cfg, "invert_rts")) invert |= INVERT_RTS; + if (cfg_getbool(cfg, "invert_cts")) invert |= INVERT_CTS; + if (cfg_getbool(cfg, "invert_dtr")) invert |= INVERT_DTR; + if (cfg_getbool(cfg, "invert_dsr")) invert |= INVERT_DSR; + if (cfg_getbool(cfg, "invert_dcd")) invert |= INVERT_DCD; + if (cfg_getbool(cfg, "invert_ri")) invert |= INVERT_RI; + eeprom_set_value(ftdi, INVERT, invert); + + if (_read > 0 || _erase > 0 || _flash > 0) + { + int vendor_id = 0, product_id = 0; + eeprom_get_value(ftdi, VENDOR_ID, &vendor_id); + eeprom_get_value(ftdi, PRODUCT_ID, &product_id); + + i = ftdi_usb_open(ftdi, vendor_id, product_id); + + if (i == 0) + { + // TODO: Do we know the eeprom size already? + printf("EEPROM size: %d\n", ftdi->eeprom->size); + } + else + { + int default_pid = cfg_getint(cfg, "default_pid"); + printf("Unable to find FTDI devices under given vendor/product id: 0x%X/0x%X\n", vendor_id, product_id); + printf("Error code: %d (%s)\n", i, ftdi_get_error_string(ftdi)); + printf("Retrying with default FTDI pid=%#04x.\n", default_pid); + + i = ftdi_usb_open(ftdi, 0x0403, default_pid); + if (i != 0) + { + printf("Error: %s\n", ftdi->error_str); + exit (-1); + } + } + } + + if (_read > 0) + { + printf("FTDI read eeprom: %d\n", ftdi_read_eeprom(ftdi)); + + ftdi_eeprom_decode(ftdi, 0); + /* Debug output */ + /* + const char* chip_types[] = {"other", "BM", "R"}; + printf("vendor_id = \"%04x\"\n", eeprom->vendor_id); + printf("product_id = \"%04x\"\n", eeprom->product_id); + printf("chip_type = \"%s\"\n", + (eeprom->chip_type > 0x06) || (eeprom->chip_type & 0x01) ? "unknown": + chip_types[eeprom->chip_type>>1]); + printf("self_powered = \"%s\"\n", eeprom->self_powered?"true":"false"); + printf("remote_wakeup = \"%s\"\n", eeprom->remote_wakeup?"true":"false"); + printf("max_power = \"%d\"\n", eeprom->max_power); + printf("in_is_isochronous = \"%s\"\n", eeprom->in_is_isochronous?"true":"false"); + printf("out_is_isochronous = \"%s\"\n", eeprom->out_is_isochronous?"true":"false"); + printf("suspend_pull_downs = \"%s\"\n", eeprom->suspend_pull_downs?"true":"false"); + printf("use_serial = \"%s\"\n", eeprom->use_serial?"true":"false"); + printf("change_usb_version = \"%s\"\n", eeprom->change_usb_version?"true":"false"); + printf("usb_version = \"%d\"\n", eeprom->usb_version); + printf("manufacturer = \"%s\"\n", eeprom->manufacturer); + printf("product = \"%s\"\n", eeprom->product); + printf("serial = \"%s\"\n", eeprom->serial); + */ + + if (filename != NULL && strlen(filename) > 0) + { + ftdi_get_eeprom_buf(ftdi, eeprom_buf, my_eeprom_size); + + FILE *fp = fopen (filename, "wb"); + fwrite (eeprom_buf, 1, my_eeprom_size, fp); + fclose (fp); + } + else + { + printf("Warning: Not writing eeprom, you must supply a valid filename\n"); + } + + goto cleanup; + } + + if (_erase > 0) + { + printf("FTDI erase eeprom: %d\n", ftdi_erase_eeprom(ftdi)); + } + + size_check = ftdi_eeprom_build(ftdi); + + if (size_check == -1) + { + printf ("Sorry, the eeprom can only contain 128 bytes (100 bytes for your strings).\n"); + printf ("You need to short your string by: %d bytes\n", size_check); + goto cleanup; + } else if (size_check < 0) { + printf ("ftdi_eeprom_build(): error: %d\n", size_check); + } + else + { + printf ("Used eeprom space: %d bytes\n", my_eeprom_size-size_check); + } + + if (_flash > 0) + { + if (cfg_getbool(cfg, "flash_raw")) + { + if (filename != NULL && strlen(filename) > 0) + { + FILE *fp = fopen(filename, "rb"); + fread(eeprom_buf, 1, my_eeprom_size, fp); + fclose(fp); + + /* TODO: Dirty hack. Create an API for this. How about ftdi_set_eeprom_buf()? */ + memcpy(ftdi->eeprom->buf, eeprom_buf, my_eeprom_size); + } + } + printf ("FTDI write eeprom: %d\n", ftdi_write_eeprom(ftdi)); + } + + // Write to file? + if (filename != NULL && strlen(filename) > 0 && !cfg_getbool(cfg, "flash_raw")) + { + fp = fopen(filename, "w"); + if (fp == NULL) + { + printf ("Can't write eeprom file.\n"); + exit (-1); + } + else + printf ("Writing to file: %s\n", filename); + + ftdi_get_eeprom_buf(ftdi, eeprom_buf, my_eeprom_size); + + fwrite(eeprom_buf, my_eeprom_size, 1, fp); + fclose(fp); + } + +cleanup: + if (_read > 0 || _erase > 0 || _flash > 0) + { + printf("FTDI close: %d\n", ftdi_usb_close(ftdi)); + } + + ftdi_deinit (ftdi); + + cfg_free(cfg); + + printf("\n"); + return 0; +} diff --git a/ftdipp/ftdi.cpp b/ftdipp/ftdi.cpp index c81eb5f..2754033 100644 --- a/ftdipp/ftdi.cpp +++ b/ftdipp/ftdi.cpp @@ -393,19 +393,9 @@ Eeprom::~Eeprom() { } -void Eeprom::init_defaults() +int Eeprom::init_defaults(char* manufacturer, char *product, char * serial) { - return ftdi_eeprom_initdefaults(&d->eeprom); -} - -void Eeprom::set_size(int size) -{ - return ftdi_eeprom_setsize(d->context, &d->eeprom, size); -} - -int Eeprom::size(unsigned char *eeprom, int maxsize) -{ - return ftdi_read_eeprom_getsize(d->context, eeprom, maxsize); + return ftdi_eeprom_initdefaults(d->context, manufacturer, product, serial); } int Eeprom::chip_id(unsigned int *chipid) @@ -415,17 +405,17 @@ int Eeprom::chip_id(unsigned int *chipid) int Eeprom::build(unsigned char *output) { - return ftdi_eeprom_build(&d->eeprom, output); + return ftdi_eeprom_build(d->context); } int Eeprom::read(unsigned char *eeprom) { - return ftdi_read_eeprom(d->context, eeprom); + return ftdi_read_eeprom(d->context); } int Eeprom::write(unsigned char *eeprom) { - return ftdi_write_eeprom(d->context, eeprom); + return ftdi_write_eeprom(d->context); } int Eeprom::read_location(int eeprom_addr, unsigned short *eeprom_val) diff --git a/ftdipp/ftdi.hpp b/ftdipp/ftdi.hpp index ba20346..3a5d993 100644 --- a/ftdipp/ftdi.hpp +++ b/ftdipp/ftdi.hpp @@ -152,9 +152,7 @@ public: Eeprom(Context* parent); ~Eeprom(); - void init_defaults(); - void set_size(int size); - int size(unsigned char *eeprom, int maxsize); + int init_defaults(char *manufacturer, char* product, char * serial); int chip_id(unsigned int *chipid); int build(unsigned char *output); diff --git a/libftdi-1.0.kdev4 b/libftdi-1.0.kdev4 new file mode 100644 index 0000000..6da24c2 --- /dev/null +++ b/libftdi-1.0.kdev4 @@ -0,0 +1,3 @@ +[Project] +Manager=KDevCMakeManager +Name=libftdi-1.0 diff --git a/libftdi.kdevelop b/libftdi.kdevelop deleted file mode 100644 index 421d88d..0000000 --- a/libftdi.kdevelop +++ /dev/null @@ -1,212 +0,0 @@ - - - - Thomas Jarosch - opensource@intra2net.com - 0.6 - KDevAutoProject - C - - C - Code - - . - false - - - - libftdi - - - - src/libftdi - default - - - src/libftdi - true - executable - / - - true - - - - - optimized - kdevgccoptions - kdevgppoptions - kdevg77options - -O2 -g0 - - - --enable-debug=full - debug - kdevgccoptions - kdevgppoptions - kdevg77options - -O0 -g3 - - - - - - - - - - - false - 1 - false - - 0 - - - - - ada - ada_bugs_gcc - bash - bash_bugs - clanlib - w3c-dom-level2-html - fortran_bugs_gcc - gnome1 - gnustep - gtk - gtk_bugs - haskell - haskell_bugs_ghc - java_bugs_gcc - java_bugs_sun - kde2book - libstdc++ - opengl - pascal_bugs_fp - php - php_bugs - perl - perl_bugs - python - python_bugs - qt-kdev3 - ruby - ruby_bugs - sdl - stl - w3c-svg - sw - w3c-uaag10 - wxwindows_bugs - - - Guide to the Qt Translation Tools - Qt Assistant Manual - Qt Designer Manual - Qt Reference Documentation - qmake User Guide - - - KDE Libraries (Doxygen) - - - - - - - - - - - - - - false - false - - - *.o,*.lo,CVS - false - false - - - - - - true - true - true - false - true - true - true - 250 - 400 - 250 - false - 0 - true - true - false - std=_GLIBCXX_STD;__gnu_cxx=std - true - false - false - false - false - true - true - false - .; - - - false - 3 - 3 - /usr/lib/qt-3.3 - EmbeddedKDevDesigner - /usr/lib/qt-3.3/bin/qmake - /usr/lib/qt-3.3/bin/designer - - - - - - .h - .cpp - - - - - - - - - - - true - false - false - - - false - true - 10 - - - - - - - - - - - true - true - true - true - -C - - diff --git a/libftdi.lnt b/libftdi.lnt index 89ad21d..2682b95 100644 --- a/libftdi.lnt +++ b/libftdi.lnt @@ -1,4 +1,5 @@ // PC-Lint 9.00 settings +--iz:\usr\include\libusb-1.0 -emacro(527, ftdi_error_return) // ignore "unreachable code" -emacro(717, ftdi_error_return) diff --git a/src/ftdi.c b/src/ftdi.c index 41496ee..c53d7aa 100644 --- a/src/ftdi.c +++ b/src/ftdi.c @@ -2,7 +2,7 @@ ftdi.c - description ------------------- begin : Fri Apr 4 2003 - copyright : (C) 2003-2010 by Intra2net AG + copyright : (C) 2003-2011 by Intra2net AG and the libftdi developers email : opensource@intra2net.com ***************************************************************************/ @@ -61,8 +61,8 @@ static void ftdi_usb_close_internal (struct ftdi_context *ftdi) { if (ftdi && ftdi->usb_dev) { - libusb_close (ftdi->usb_dev); - ftdi->usb_dev = NULL; + libusb_close (ftdi->usb_dev); + ftdi->usb_dev = NULL; } } @@ -73,11 +73,13 @@ static void ftdi_usb_close_internal (struct ftdi_context *ftdi) \retval 0: all fine \retval -1: couldn't allocate read buffer + \retval -2: couldn't allocate struct buffer \remark This should be called before all functions */ int ftdi_init(struct ftdi_context *ftdi) { + struct ftdi_eeprom* eeprom = (struct ftdi_eeprom *)malloc(sizeof(struct ftdi_eeprom)); ftdi->usb_ctx = NULL; ftdi->usb_dev = NULL; ftdi->usb_read_timeout = 5000; @@ -98,7 +100,10 @@ int ftdi_init(struct ftdi_context *ftdi) ftdi->error_str = NULL; - ftdi->eeprom_size = FTDI_DEFAULT_EEPROM_SIZE; + if (eeprom == 0) + ftdi_error_return(-2, "Can't malloc struct ftdi_eeprom"); + memset(eeprom, 0, sizeof(struct ftdi_eeprom)); + ftdi->eeprom = eeprom; /* All fine. Now allocate the readbuffer */ return ftdi_read_data_set_chunksize(ftdi, 4096); @@ -192,6 +197,27 @@ void ftdi_deinit(struct ftdi_context *ftdi) free(ftdi->readbuffer); ftdi->readbuffer = NULL; } + + if (ftdi->eeprom != NULL) + { + if (ftdi->eeprom->manufacturer != 0) + { + free(ftdi->eeprom->manufacturer); + ftdi->eeprom->manufacturer = 0; + } + if (ftdi->eeprom->product != 0) + { + free(ftdi->eeprom->product); + ftdi->eeprom->product = 0; + } + if (ftdi->eeprom->serial != 0) + { + free(ftdi->eeprom->serial); + ftdi->eeprom->serial = 0; + } + free(ftdi->eeprom); + ftdi->eeprom = NULL; + } libusb_exit(ftdi->usb_ctx); } @@ -265,7 +291,7 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli *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; @@ -485,7 +511,7 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev) if (libusb_set_configuration(ftdi->usb_dev, cfg0) < 0) { ftdi_usb_close_internal (ftdi); - if(detach_errno == EPERM) + if (detach_errno == EPERM) { ftdi_error_return(-8, "inappropriate permissions on device!"); } @@ -499,7 +525,7 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev) if (libusb_claim_interface(ftdi->usb_dev, ftdi->interface) < 0) { ftdi_usb_close_internal (ftdi); - if(detach_errno == EPERM) + if (detach_errno == EPERM) { ftdi_error_return(-8, "inappropriate permissions on device!"); } @@ -518,7 +544,7 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev) // Try to guess chip type // Bug in the BM type chips: bcdDevice is 0x200 for serial == 0 if (desc.bcdDevice == 0x400 || (desc.bcdDevice == 0x200 - && desc.iSerialNumber == 0)) + && desc.iSerialNumber == 0)) ftdi->type = TYPE_BM; else if (desc.bcdDevice == 0x200) ftdi->type = TYPE_AM; @@ -610,7 +636,7 @@ int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product, \retval -11: ftdi context invalid */ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product, - const char* description, const char* serial, unsigned int index) + const char* description, const char* serial, unsigned int index) { libusb_device *dev; libusb_device **devs; @@ -668,11 +694,11 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product, ftdi_usb_close_internal (ftdi); - if (index > 0) - { - index--; - continue; - } + if (index > 0) + { + index--; + continue; + } res = ftdi_usb_open_dev(ftdi, dev); libusb_free_device_list(devs,1); @@ -723,24 +749,24 @@ int ftdi_usb_open_string(struct ftdi_context *ftdi, const char* description) { libusb_device *dev; libusb_device **devs; - unsigned int bus_number, device_address; - int i = 0; + unsigned int bus_number, device_address; + int i = 0; if (libusb_init (&ftdi->usb_ctx) < 0) - ftdi_error_return(-1, "libusb_init() failed"); + ftdi_error_return(-1, "libusb_init() failed"); - if (libusb_get_device_list(ftdi->usb_ctx, &devs) < 0) - ftdi_error_return(-2, "libusb_get_device_list() failed"); + if (libusb_get_device_list(ftdi->usb_ctx, &devs) < 0) + ftdi_error_return(-2, "libusb_get_device_list() failed"); /* XXX: This doesn't handle symlinks/odd paths/etc... */ if (sscanf (description + 2, "%u/%u", &bus_number, &device_address) != 2) - ftdi_error_return_free_device_list(-11, "illegal description format", devs); + ftdi_error_return_free_device_list(-11, "illegal description format", devs); - while ((dev = devs[i++]) != NULL) + while ((dev = devs[i++]) != NULL) { int ret; - if (bus_number == libusb_get_bus_number (dev) - && device_address == libusb_get_device_address (dev)) + if (bus_number == libusb_get_bus_number (dev) + && device_address == libusb_get_device_address (dev)) { ret = ftdi_usb_open_dev(ftdi, dev); libusb_free_device_list(devs,1); @@ -1247,9 +1273,9 @@ static void ftdi_read_data_cb(struct libusb_transfer *transfer) if (actual_length > packet_size - 2) { for (i = 1; i < num_of_chunks; i++) - memmove (ftdi->readbuffer+ftdi->readbuffer_offset+(packet_size - 2)*i, - ftdi->readbuffer+ftdi->readbuffer_offset+packet_size*i, - packet_size - 2); + memmove (ftdi->readbuffer+ftdi->readbuffer_offset+(packet_size - 2)*i, + ftdi->readbuffer+ftdi->readbuffer_offset+packet_size*i, + packet_size - 2); if (chunk_remains > 2) { memmove (ftdi->readbuffer+ftdi->readbuffer_offset+(packet_size - 2)*i, @@ -1258,7 +1284,7 @@ static void ftdi_read_data_cb(struct libusb_transfer *transfer) actual_length -= 2*num_of_chunks; } else - actual_length -= 2*(num_of_chunks-1)+chunk_remains; + actual_length -= 2*(num_of_chunks-1)+chunk_remains; } if (actual_length > 0) @@ -1309,9 +1335,9 @@ static void ftdi_write_data_cb(struct libusb_transfer *transfer) { struct ftdi_transfer_control *tc = (struct ftdi_transfer_control *) transfer->user_data; struct ftdi_context *ftdi = tc->ftdi; - + tc->offset += transfer->actual_length; - + if (tc->offset == tc->size) { tc->completed = 1; @@ -1350,19 +1376,22 @@ static void ftdi_write_data_cb(struct libusb_transfer *transfer) struct ftdi_transfer_control *ftdi_write_data_submit(struct ftdi_context *ftdi, unsigned char *buf, int size) { struct ftdi_transfer_control *tc; - struct libusb_transfer *transfer = libusb_alloc_transfer(0); + struct libusb_transfer *transfer; int write_size, ret; if (ftdi == NULL || ftdi->usb_dev == NULL) - { - libusb_free_transfer(transfer); return NULL; - } tc = (struct ftdi_transfer_control *) malloc (sizeof (*tc)); + if (!tc) + return NULL; - if (!tc || !transfer) + transfer = libusb_alloc_transfer(0); + if (!transfer) + { + free(tc); return NULL; + } tc->ftdi = ftdi; tc->completed = 0; @@ -1371,9 +1400,9 @@ struct ftdi_transfer_control *ftdi_write_data_submit(struct ftdi_context *ftdi, tc->offset = 0; if (size < ftdi->writebuffer_chunksize) - write_size = size; + write_size = size; else - write_size = ftdi->writebuffer_chunksize; + write_size = ftdi->writebuffer_chunksize; libusb_fill_bulk_transfer(transfer, ftdi->usb_dev, ftdi->in_ep, buf, write_size, ftdi_write_data_cb, tc, @@ -1384,8 +1413,7 @@ struct ftdi_transfer_control *ftdi_write_data_submit(struct ftdi_context *ftdi, if (ret < 0) { libusb_free_transfer(transfer); - tc->completed = 1; - tc->transfer = NULL; + free(tc); return NULL; } tc->transfer = transfer; @@ -2140,109 +2168,126 @@ int ftdi_set_error_char(struct ftdi_context *ftdi, } /** - Set the eeprom size - - \param ftdi pointer to ftdi_context - \param eeprom Pointer to ftdi_eeprom - \param size - -*/ -void ftdi_eeprom_setsize(struct ftdi_context *ftdi, struct ftdi_eeprom *eeprom, int size) -{ - if (ftdi == NULL) - return; - - ftdi->eeprom_size=size; - eeprom->size=size; -} - -/** Init eeprom with default values. + \param ftdi pointer to ftdi_context + \param manufacturer String to use as Manufacturer + \param product String to use as Product description + \param serial String to use as Serial number description - \param eeprom Pointer to ftdi_eeprom + \retval 0: all fine + \retval -1: No struct ftdi_context + \retval -2: No struct ftdi_eeprom */ -void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom) +int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer, + char * product, char * serial) { - int i; - - if (eeprom == NULL) - return; + struct ftdi_eeprom *eeprom; - eeprom->vendor_id = 0x0403; - eeprom->product_id = 0x6001; + if (ftdi == NULL) + ftdi_error_return(-1, "No struct ftdi_context"); - eeprom->self_powered = 1; - eeprom->remote_wakeup = 1; - eeprom->chip_type = TYPE_BM; + if (ftdi->eeprom == NULL) + ftdi_error_return(-2,"No struct ftdi_eeprom"); - eeprom->in_is_isochronous = 0; - eeprom->out_is_isochronous = 0; - eeprom->suspend_pull_downs = 0; + eeprom = ftdi->eeprom; + memset(eeprom, 0, sizeof(struct ftdi_eeprom)); - eeprom->use_serial = 0; - eeprom->change_usb_version = 0; - eeprom->usb_version = 0x0200; - eeprom->max_power = 0; + eeprom->vendor_id = 0x0403; + eeprom->use_serial = USE_SERIAL_NUM; + if ((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM) || + (ftdi->type == TYPE_R)) + eeprom->product_id = 0x6001; + else + eeprom->product_id = 0x6010; + if (ftdi->type == TYPE_AM) + eeprom->usb_version = 0x0101; + else + eeprom->usb_version = 0x0200; + eeprom->max_power = 100; + if (eeprom->manufacturer) + free (eeprom->manufacturer); eeprom->manufacturer = NULL; + if (manufacturer) + { + eeprom->manufacturer = malloc(strlen(manufacturer)+1); + if (eeprom->manufacturer) + strcpy(eeprom->manufacturer, manufacturer); + } + + if (eeprom->product) + free (eeprom->product); eeprom->product = NULL; - eeprom->serial = NULL; - for (i=0; i < 5; i++) { - eeprom->cbus_function[i] = 0; + eeprom->product = malloc(strlen(product)+1); + if (eeprom->product) + strcpy(eeprom->product, product); } - eeprom->high_current = 0; - eeprom->invert = 0; - eeprom->size = FTDI_DEFAULT_EEPROM_SIZE; -} + if (eeprom->serial) + free (eeprom->serial); + eeprom->serial = NULL; + if (serial) + { + eeprom->serial = malloc(strlen(serial)+1); + if (eeprom->serial) + strcpy(eeprom->serial, serial); + } -/** - Frees allocated memory in eeprom. - \param eeprom Pointer to ftdi_eeprom -*/ -void ftdi_eeprom_free(struct ftdi_eeprom *eeprom) -{ - if (eeprom->manufacturer != 0) { - free(eeprom->manufacturer); - eeprom->manufacturer = 0; - } - if (eeprom->product != 0) { - free(eeprom->product); - eeprom->product = 0; - } - if (eeprom->serial != 0) { - free(eeprom->serial); - eeprom->serial = 0; + if (ftdi->type == TYPE_R) + { + eeprom->max_power = 90; + eeprom->size = 0x80; + eeprom->cbus_function[0] = CBUS_TXLED; + eeprom->cbus_function[1] = CBUS_RXLED; + eeprom->cbus_function[2] = CBUS_TXDEN; + eeprom->cbus_function[3] = CBUS_PWREN; + eeprom->cbus_function[4] = CBUS_SLEEP; } + else + eeprom->size = -1; + return 0; } /** - Build binary output from ftdi_eeprom structure. + Build binary buffer from ftdi_eeprom structure. Output is suitable for ftdi_write_eeprom(). - \note This function doesn't handle FT2232x devices. Only FT232x. - \param eeprom Pointer to ftdi_eeprom - \param output Buffer of 128 bytes to store eeprom image to + \param ftdi pointer to ftdi_context - \retval >0: free eeprom size + \retval >=0: size of eeprom user area in bytes \retval -1: eeprom size (128 bytes) exceeded by custom strings - \retval -2: Invalid eeprom pointer - \retval -3: Invalid cbus function setting - \retval -4: Chip doesn't support invert - \retval -5: Chip doesn't support high current drive + \retval -2: Invalid eeprom or ftdi pointer + \retval -3: Invalid cbus function setting (FIXME: Not in the code?) + \retval -4: Chip doesn't support invert (FIXME: Not in the code?) + \retval -5: Chip doesn't support high current drive (FIXME: Not in the code?) + \retval -6: No connected EEPROM or EEPROM Type unknown */ -int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) +int ftdi_eeprom_build(struct ftdi_context *ftdi) { - unsigned char i, j; + unsigned char i, j, eeprom_size_mask; unsigned short checksum, value; unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0; - int size_check; - const int cbus_max[5] = {13, 13, 13, 13, 9}; + int user_area_size; + struct ftdi_eeprom *eeprom; + unsigned char * output; - if (eeprom == NULL) - return -2; + if (ftdi == NULL) + ftdi_error_return(-2,"No context"); + if (ftdi->eeprom == NULL) + ftdi_error_return(-2,"No eeprom structure"); + + eeprom= ftdi->eeprom; + output = eeprom->buf; + + if (eeprom->chip == -1) + ftdi_error_return(-6,"No connected EEPROM or EEPROM type unknown"); + + if ((eeprom->chip == 0x56) || (eeprom->chip == 0x66)) + eeprom->size = 0x100; + else + eeprom->size = 0x80; if (eeprom->manufacturer != NULL) manufacturer_size = strlen(eeprom->manufacturer); @@ -2251,43 +2296,37 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) if (eeprom->serial != NULL) serial_size = strlen(eeprom->serial); - // highest allowed cbus value - for (i = 0; i < 5; i++) - { - if ((eeprom->cbus_function[i] > cbus_max[i]) || - (eeprom->cbus_function[i] && eeprom->chip_type != TYPE_R)) return -3; - } - if (eeprom->chip_type != TYPE_R) + // eeprom size check + switch (ftdi->type) { - if (eeprom->invert) return -4; - if (eeprom->high_current) return -5; + case TYPE_AM: + case TYPE_BM: + user_area_size = 96; // base size for strings (total of 48 characters) + break; + case TYPE_2232C: + user_area_size = 90; // two extra config bytes and 4 bytes PnP stuff + break; + case TYPE_R: + user_area_size = 88; // four extra config bytes + 4 bytes PnP stuff + break; + case TYPE_2232H: // six extra config bytes + 4 bytes PnP stuff + case TYPE_4232H: + user_area_size = 86; + break; + default: + user_area_size = 0; + break; } + user_area_size -= (manufacturer_size + product_size + serial_size) * 2; - size_check = eeprom->size; - size_check -= 28; // 28 are always in use (fixed) - - // Top half of a 256byte eeprom is used just for strings and checksum - // it seems that the FTDI chip will not read these strings from the lower half - // Each string starts with two bytes; offset and type (0x03 for string) - // the checksum needs two bytes, so without the string data that 8 bytes from the top half - if (eeprom->size>=256) size_check = 120; - size_check -= manufacturer_size*2; - size_check -= product_size*2; - size_check -= serial_size*2; - - // eeprom size exceeded? - if (size_check < 0) - return (-1); + if (user_area_size < 0) + ftdi_error_return(-1,"eeprom size exceeded"); // empty eeprom - memset (output, 0, eeprom->size); + memset (ftdi->eeprom->buf, 0, FTDI_MAX_EEPROM_SIZE); + + // Bytes and Bits set for all Types - // Addr 00: High current IO - output[0x00] = eeprom->high_current ? HIGH_CURRENT_DRIVE : 0; - // Addr 01: IN endpoint size (for R type devices, different for FT2232) - if (eeprom->chip_type == TYPE_R) { - output[0x01] = 0x40; - } // Addr 02: Vendor ID output[0x02] = eeprom->vendor_id; output[0x03] = eeprom->vendor_id >> 8; @@ -2298,7 +2337,8 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) // Addr 06: Device release number (0400h for BM features) output[0x06] = 0x00; - switch (eeprom->chip_type) { + switch (ftdi->type) + { case TYPE_AM: output[0x07] = 0x02; break; @@ -2311,6 +2351,12 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) case TYPE_R: output[0x07] = 0x06; break; + case TYPE_2232H: + output[0x07] = 0x07; + break; + case TYPE_4232H: + output[0x07] = 0x08; + break; default: output[0x07] = 0x00; } @@ -2319,7 +2365,7 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) // Bit 7: always 1 // Bit 6: 1 if this device is self powered, 0 if bus powered // Bit 5: 1 if this device uses remote wakeup - // Bit 4: 1 if this device is battery powered + // Bit 4-0: reserved - 0 j = 0x80; if (eeprom->self_powered == 1) j |= 0x40; @@ -2328,102 +2374,266 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) output[0x08] = j; // Addr 09: Max power consumption: max power = value * 2 mA - output[0x09] = eeprom->max_power; + output[0x09] = eeprom->max_power>>1; - // 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: Invert data lines - output[0x0B] = eeprom->invert & 0xff; - - // 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) + if (ftdi->type != TYPE_AM) { - output[0x0C] = eeprom->usb_version; - output[0x0D] = eeprom->usb_version >> 8; + // 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; + output[0x0A] = j; } + // Dynamic content + // Strings start at 0x94 (TYPE_AM, TYPE_BM) + // 0x96 (TYPE_2232C), 0x98 (TYPE_R) and 0x9a (TYPE_x232H) + i = 0; + switch (ftdi->type) + { + case TYPE_2232H: + case TYPE_4232H: + i += 2; + case TYPE_R: + i += 2; + case TYPE_2232C: + i += 2; + case TYPE_AM: + case TYPE_BM: + i += 0x94; + } + /* Wrap around 0x80 for 128 byte EEPROMS (Internale and 93x46) */ + eeprom_size_mask = eeprom->size -1; // Addr 0E: Offset of the manufacturer string + 0x80, calculated later // Addr 0F: Length of manufacturer string + // Output manufacturer + output[0x0E] = i; // calculate offset + output[i & eeprom_size_mask] = manufacturer_size*2 + 2, i++; + output[i & eeprom_size_mask] = 0x03, i++; // type: string + for (j = 0; j < manufacturer_size; j++) + { + output[i & eeprom_size_mask] = eeprom->manufacturer[j], i++; + output[i & eeprom_size_mask] = 0x00, i++; + } output[0x0F] = manufacturer_size*2 + 2; // Addr 10: Offset of the product string + 0x80, calculated later // Addr 11: Length of product string + output[0x10] = i | 0x80; // calculate offset + output[i & eeprom_size_mask] = product_size*2 + 2, i++; + output[i & eeprom_size_mask] = 0x03, i++; + for (j = 0; j < product_size; j++) + { + output[i & eeprom_size_mask] = eeprom->product[j], i++; + output[i & eeprom_size_mask] = 0x00, i++; + } 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; - - // Addr 14: CBUS function: CBUS0, CBUS1 - // Addr 15: CBUS function: CBUS2, CBUS3 - // Addr 16: CBUS function: CBUS5 - output[0x14] = eeprom->cbus_function[0] | (eeprom->cbus_function[1] << 4); - output[0x15] = eeprom->cbus_function[2] | (eeprom->cbus_function[3] << 4); - output[0x16] = eeprom->cbus_function[4]; - // Addr 17: Unknown - - // Dynamic content - // In images produced by FTDI's FT_Prog for FT232R strings start at 0x18 - // Space till 0x18 should be considered as reserved. - if (eeprom->chip_type >= TYPE_R) { - i = 0x18; - } else { - i = 0x14; + output[0x12] = i | 0x80; // calculate offset + output[i & eeprom_size_mask] = serial_size*2 + 2, i++; + output[i & eeprom_size_mask] = 0x03, i++; + for (j = 0; j < serial_size; j++) + { + output[i & eeprom_size_mask] = eeprom->serial[j], i++; + output[i & eeprom_size_mask] = 0x00, i++; } - if (eeprom->size >= 256) i = 0x80; - - // Output manufacturer - output[0x0E] = i | 0x80; // calculate offset - output[i++] = manufacturer_size*2 + 2; - output[i++] = 0x03; // type: string - for (j = 0; j < manufacturer_size; j++) + // Legacy port name and PnP fields for FT2232 and newer chips + if (ftdi->type > TYPE_BM) { - output[i] = eeprom->manufacturer[j], i++; - output[i] = 0x00, i++; + output[i & eeprom_size_mask] = 0x02; /* as seen when written with FTD2XX */ + i++; + output[i & eeprom_size_mask] = 0x03; /* as seen when written with FTD2XX */ + i++; + output[i & eeprom_size_mask] = eeprom->is_not_pnp; /* as seen when written with FTD2XX */ + 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[0x13] = serial_size*2 + 2; + + if (ftdi->type > TYPE_AM) /* use_serial not used in AM devices */ { - output[i] = eeprom->product[j], i++; - output[i] = 0x00, i++; + if (eeprom->use_serial == USE_SERIAL_NUM ) + output[0x0A] |= USE_SERIAL_NUM; + else + output[0x0A] &= ~USE_SERIAL_NUM; } - // 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++) + /* Bytes and Bits specific to (some) types + Write linear, as this allows easier fixing*/ + switch (ftdi->type) { - output[i] = eeprom->serial[j], i++; - output[i] = 0x00, i++; + case TYPE_AM: + break; + case TYPE_BM: + output[0x0C] = eeprom->usb_version & 0xff; + output[0x0D] = (eeprom->usb_version>>8) & 0xff; + if (eeprom->use_usb_version == USE_USB_VERSION_BIT) + output[0x0A] |= USE_USB_VERSION_BIT; + else + output[0x0A] &= ~USE_USB_VERSION_BIT; + + break; + case TYPE_2232C: + + output[0x00] = (eeprom->channel_a_type); + if ( eeprom->channel_a_driver == DRIVER_VCP) + output[0x00] |= DRIVER_VCP; + else + output[0x00] &= ~DRIVER_VCP; + + if ( eeprom->high_current_a == HIGH_CURRENT_DRIVE) + output[0x00] |= HIGH_CURRENT_DRIVE; + else + output[0x00] &= ~HIGH_CURRENT_DRIVE; + + output[0x01] = (eeprom->channel_b_type); + if ( eeprom->channel_b_driver == DRIVER_VCP) + output[0x01] |= DRIVER_VCP; + else + output[0x01] &= ~DRIVER_VCP; + + if ( eeprom->high_current_b == HIGH_CURRENT_DRIVE) + output[0x01] |= HIGH_CURRENT_DRIVE; + else + output[0x01] &= ~HIGH_CURRENT_DRIVE; + + if (eeprom->in_is_isochronous == 1) + output[0x0A] |= 0x1; + else + output[0x0A] &= ~0x1; + if (eeprom->out_is_isochronous == 1) + output[0x0A] |= 0x2; + else + output[0x0A] &= ~0x2; + if (eeprom->suspend_pull_downs == 1) + output[0x0A] |= 0x4; + else + output[0x0A] &= ~0x4; + if (eeprom->use_usb_version == USE_USB_VERSION_BIT) + output[0x0A] |= USE_USB_VERSION_BIT; + else + output[0x0A] &= ~USE_USB_VERSION_BIT; + + output[0x0C] = eeprom->usb_version & 0xff; + output[0x0D] = (eeprom->usb_version>>8) & 0xff; + output[0x14] = eeprom->chip; + break; + case TYPE_R: + if (eeprom->high_current == HIGH_CURRENT_DRIVE_R) + output[0x00] |= HIGH_CURRENT_DRIVE_R; + output[0x01] = 0x40; /* Hard coded Endpoint Size*/ + + if (eeprom->suspend_pull_downs == 1) + output[0x0A] |= 0x4; + else + output[0x0A] &= ~0x4; + output[0x0B] = eeprom->invert; + output[0x0C] = eeprom->usb_version & 0xff; + output[0x0D] = (eeprom->usb_version>>8) & 0xff; + + if (eeprom->cbus_function[0] > CBUS_BB) + output[0x14] = CBUS_TXLED; + else + output[0x14] = eeprom->cbus_function[0]; + + if (eeprom->cbus_function[1] > CBUS_BB) + output[0x14] |= CBUS_RXLED<<4; + else + output[0x14] |= eeprom->cbus_function[1]<<4; + + if (eeprom->cbus_function[2] > CBUS_BB) + output[0x15] = CBUS_TXDEN; + else + output[0x15] = eeprom->cbus_function[2]; + + if (eeprom->cbus_function[3] > CBUS_BB) + output[0x15] |= CBUS_PWREN<<4; + else + output[0x15] |= eeprom->cbus_function[3]<<4; + + if (eeprom->cbus_function[4] > CBUS_CLK6) + output[0x16] = CBUS_SLEEP; + else + output[0x16] = eeprom->cbus_function[4]; + break; + case TYPE_2232H: + output[0x00] = (eeprom->channel_a_type); + if ( eeprom->channel_a_driver == DRIVER_VCP) + output[0x00] |= DRIVER_VCP; + else + output[0x00] &= ~DRIVER_VCP; + + output[0x01] = (eeprom->channel_b_type); + if ( eeprom->channel_b_driver == DRIVER_VCP) + output[0x01] |= DRIVER_VCP; + else + output[0x01] &= ~DRIVER_VCP; + if (eeprom->suspend_dbus7 == SUSPEND_DBUS7_BIT) + output[0x01] |= SUSPEND_DBUS7_BIT; + else + output[0x01] &= ~SUSPEND_DBUS7_BIT; + + if (eeprom->suspend_pull_downs == 1) + output[0x0A] |= 0x4; + else + output[0x0A] &= ~0x4; + + if (eeprom->group0_drive > DRIVE_16MA) + output[0x0c] |= DRIVE_16MA; + else + output[0x0c] |= eeprom->group0_drive; + if (eeprom->group0_schmitt == IS_SCHMITT) + output[0x0c] |= IS_SCHMITT; + if (eeprom->group0_slew == SLOW_SLEW) + output[0x0c] |= SLOW_SLEW; + + if (eeprom->group1_drive > DRIVE_16MA) + output[0x0c] |= DRIVE_16MA<<4; + else + output[0x0c] |= eeprom->group1_drive<<4; + if (eeprom->group1_schmitt == IS_SCHMITT) + output[0x0c] |= IS_SCHMITT<<4; + if (eeprom->group1_slew == SLOW_SLEW) + output[0x0c] |= SLOW_SLEW<<4; + + if (eeprom->group2_drive > DRIVE_16MA) + output[0x0d] |= DRIVE_16MA; + else + output[0x0d] |= eeprom->group2_drive; + if (eeprom->group2_schmitt == IS_SCHMITT) + output[0x0d] |= IS_SCHMITT; + if (eeprom->group2_slew == SLOW_SLEW) + output[0x0d] |= SLOW_SLEW; + + if (eeprom->group3_drive > DRIVE_16MA) + output[0x0d] |= DRIVE_16MA<<4; + else + output[0x0d] |= eeprom->group3_drive<<4; + if (eeprom->group3_schmitt == IS_SCHMITT) + output[0x0d] |= IS_SCHMITT<<4; + if (eeprom->group3_slew == SLOW_SLEW) + output[0x0d] |= SLOW_SLEW<<4; + + output[0x18] = eeprom->chip; + + break; + case TYPE_4232H: + fprintf(stderr,"FIXME: Build FT4232H specific EEPROM settings\n"); } // calculate checksum @@ -2441,15 +2651,14 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) output[eeprom->size-2] = checksum; output[eeprom->size-1] = checksum >> 8; - return size_check; + return user_area_size; } /** Decode binary EEPROM image into an ftdi_eeprom structure. - \param eeprom Pointer to ftdi_eeprom which will be filled in. - \param buf Buffer of \a size bytes of raw eeprom data - \param size size size of eeprom data in bytes + \param ftdi pointer to ftdi_context + \param verbose Decode EEPROM on stdout \retval 0: all fine \retval -1: something went wrong @@ -2457,38 +2666,23 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output) FIXME: How to pass size? How to handle size field in ftdi_eeprom? FIXME: Strings are malloc'ed here and should be freed somewhere */ -int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size) +int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose) { unsigned char i, j; unsigned short checksum, eeprom_checksum, value; unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0; - int eeprom_size = 128; - - if (eeprom == NULL) - return -1; -#if 0 - size_check = eeprom->size; - size_check -= 28; // 28 are always in use (fixed) - - // Top half of a 256byte eeprom is used just for strings and checksum - // it seems that the FTDI chip will not read these strings from the lower half - // Each string starts with two bytes; offset and type (0x03 for string) - // the checksum needs two bytes, so without the string data that 8 bytes from the top half - if (eeprom->size>=256)size_check = 120; - size_check -= manufacturer_size*2; - size_check -= product_size*2; - size_check -= serial_size*2; - - // eeprom size exceeded? - if (size_check < 0) - return (-1); -#endif + int eeprom_size; + struct ftdi_eeprom *eeprom; + unsigned char *buf = ftdi->eeprom->buf; + int release; - // empty eeprom struct - memset(eeprom, 0, sizeof(struct ftdi_eeprom)); + if (ftdi == NULL) + ftdi_error_return(-1,"No context"); + if (ftdi->eeprom == NULL) + ftdi_error_return(-1,"No eeprom structure"); - // Addr 00: High current IO - eeprom->high_current = (buf[0x02] & HIGH_CURRENT_DRIVE); + eeprom = ftdi->eeprom; + eeprom_size = eeprom->size; // Addr 02: Vendor ID eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8); @@ -2496,31 +2690,14 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size) // Addr 04: Product ID eeprom->product_id = buf[0x04] + (buf[0x05] << 8); - value = buf[0x06] + (buf[0x07]<<8); - switch (value) - { - case 0x0600: - eeprom->chip_type = TYPE_R; - break; - case 0x0400: - eeprom->chip_type = TYPE_BM; - break; - case 0x0200: - eeprom->chip_type = TYPE_AM; - break; - default: // Unknown device - eeprom->chip_type = 0; - break; - } + release = buf[0x06] + (buf[0x07]<<8); // Addr 08: Config descriptor // Bit 7: always 1 // Bit 6: 1 if this device is self powered, 0 if bus powered // Bit 5: 1 if this device uses remote wakeup - // Bit 4: 1 if this device is battery powered - j = buf[0x08]; - if (j&0x40) eeprom->self_powered = 1; - if (j&0x20) eeprom->remote_wakeup = 1; + eeprom->self_powered = buf[0x08] & 0x40; + eeprom->remote_wakeup = buf[0x08] & 0x20; // Addr 09: Max power consumption: max power = value * 2 mA eeprom->max_power = buf[0x09]; @@ -2529,83 +2706,84 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size) // Bit 7: 0 - reserved // Bit 6: 0 - reserved // Bit 5: 0 - reserved - // Bit 4: 1 - Change USB version + // Bit 4: 1 - Change USB version on BM and 2232C // 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 = buf[0x0A]; - if (j&0x01) eeprom->in_is_isochronous = 1; - if (j&0x02) eeprom->out_is_isochronous = 1; - if (j&0x04) eeprom->suspend_pull_downs = 1; - if (j&0x08) eeprom->use_serial = 1; - if (j&0x10) eeprom->change_usb_version = 1; + eeprom->in_is_isochronous = buf[0x0A]&0x01; + eeprom->out_is_isochronous = buf[0x0A]&0x02; + eeprom->suspend_pull_downs = buf[0x0A]&0x04; + eeprom->use_serial = buf[0x0A] & USE_SERIAL_NUM; + eeprom->use_usb_version = buf[0x0A] & USE_USB_VERSION_BIT; - // Addr 0B: Invert data lines - eeprom->invert = buf[0x0B]; - - // 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) - { - eeprom->usb_version = buf[0x0C] + (buf[0x0D] << 8); - } + // Addr 0C: USB version low byte when 0x0A + // Addr 0D: USB version high byte when 0x0A + eeprom->usb_version = buf[0x0C] + (buf[0x0D] << 8); // Addr 0E: Offset of the manufacturer string + 0x80, calculated later // Addr 0F: Length of manufacturer string manufacturer_size = buf[0x0F]/2; - if (manufacturer_size > 0) eeprom->manufacturer = malloc(manufacturer_size); + if (eeprom->manufacturer) + free(eeprom->manufacturer); + if (manufacturer_size > 0) + { + eeprom->manufacturer = malloc(manufacturer_size); + if (eeprom->manufacturer) + { + // Decode manufacturer + i = buf[0x0E] & (eeprom_size -1); // offset + for (j=0;jmanufacturer[j] = buf[2*j+i+2]; + } + eeprom->manufacturer[j] = '\0'; + } + } else eeprom->manufacturer = NULL; // Addr 10: Offset of the product string + 0x80, calculated later // Addr 11: Length of product string + if (eeprom->product) + free(eeprom->product); product_size = buf[0x11]/2; - if (product_size > 0) eeprom->product = malloc(product_size); + if (product_size > 0) + { + eeprom->product = malloc(product_size); + if (eeprom->product) + { + // Decode product name + i = buf[0x10] & (eeprom_size -1); // offset + for (j=0;jproduct[j] = buf[2*j+i+2]; + } + eeprom->product[j] = '\0'; + } + } else eeprom->product = NULL; // Addr 12: Offset of the serial string + 0x80, calculated later // Addr 13: Length of serial string + if (eeprom->serial) + free(eeprom->serial); serial_size = buf[0x13]/2; - if (serial_size > 0) eeprom->serial = malloc(serial_size); - else eeprom->serial = NULL; - - // Addr 14: CBUS function: CBUS0, CBUS1 - // Addr 15: CBUS function: CBUS2, CBUS3 - // Addr 16: CBUS function: CBUS5 - if (eeprom->chip_type == TYPE_R) { - eeprom->cbus_function[0] = buf[0x14] & 0x0f; - eeprom->cbus_function[1] = (buf[0x14] >> 4) & 0x0f; - eeprom->cbus_function[2] = buf[0x15] & 0x0f; - eeprom->cbus_function[3] = (buf[0x15] >> 4) & 0x0f; - eeprom->cbus_function[4] = buf[0x16] & 0x0f; - } else { - for (j=0; j<5; j++) eeprom->cbus_function[j] = 0; - } - - // Decode manufacturer - i = buf[0x0E] & 0x7f; // offset - for (j=0;j 0) { - eeprom->manufacturer[j] = buf[2*j+i+2]; - } - eeprom->manufacturer[j] = '\0'; - - // Decode product name - i = buf[0x10] & 0x7f; // offset - for (j=0;jproduct[j] = buf[2*j+i+2]; - } - eeprom->product[j] = '\0'; - - // Decode serial - i = buf[0x12] & 0x7f; // offset - for (j=0;jserial[j] = buf[2*j+i+2]; + eeprom->serial = malloc(serial_size); + if (eeprom->serial) + { + // Decode serial + i = buf[0x12] & (eeprom_size -1); // offset + for (j=0;jserial[j] = buf[2*j+i+2]; + } + eeprom->serial[j] = '\0'; + } } - eeprom->serial[j] = '\0'; + else eeprom->serial = NULL; // verify checksum checksum = 0xAAAA; @@ -2624,8 +2802,471 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size) if (eeprom_checksum != checksum) { fprintf(stderr, "Checksum Error: %04x %04x\n", checksum, eeprom_checksum); - return -1; + ftdi_error_return(-1,"EEPROM checksum error"); + } + + eeprom->channel_a_type = 0; + if ((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM)) + { + eeprom->chip = -1; + } + else if (ftdi->type == TYPE_2232C) + { + eeprom->channel_a_type = buf[0x00] & 0x7; + eeprom->channel_a_driver = buf[0x00] & DRIVER_VCP; + eeprom->high_current_a = buf[0x00] & HIGH_CURRENT_DRIVE; + eeprom->channel_b_type = buf[0x01] & 0x7; + eeprom->channel_b_driver = buf[0x01] & DRIVER_VCP; + eeprom->high_current_b = buf[0x01] & HIGH_CURRENT_DRIVE; + eeprom->chip = buf[0x14]; + } + else if (ftdi->type == TYPE_R) + { + /* TYPE_R flags D2XX, not VCP as all others*/ + eeprom->channel_a_driver = (~buf[0x00]) & DRIVER_VCP; + eeprom->high_current = buf[0x00] & HIGH_CURRENT_DRIVE_R; + if ( (buf[0x01]&0x40) != 0x40) + fprintf(stderr, + "TYPE_R EEPROM byte[0x01] Bit 6 unexpected Endpoint size." + " If this happened with the\n" + " EEPROM programmed by FTDI tools, please report " + "to libftdi@developer.intra2net.com\n"); + + eeprom->chip = buf[0x16]; + // Addr 0B: Invert data lines + // Works only on FT232R, not FT245R, but no way to distinguish + eeprom->invert = buf[0x0B]; + // Addr 14: CBUS function: CBUS0, CBUS1 + // Addr 15: CBUS function: CBUS2, CBUS3 + // Addr 16: CBUS function: CBUS5 + eeprom->cbus_function[0] = buf[0x14] & 0x0f; + eeprom->cbus_function[1] = (buf[0x14] >> 4) & 0x0f; + eeprom->cbus_function[2] = buf[0x15] & 0x0f; + eeprom->cbus_function[3] = (buf[0x15] >> 4) & 0x0f; + eeprom->cbus_function[4] = buf[0x16] & 0x0f; + } + else if ((ftdi->type == TYPE_2232H) ||(ftdi->type == TYPE_4232H)) + { + eeprom->channel_a_type = buf[0x00] & 0x7; + eeprom->channel_a_driver = buf[0x00] & DRIVER_VCP; + eeprom->channel_b_type = buf[0x01] & 0x7; + eeprom->channel_b_driver = buf[0x01] & DRIVER_VCP; + + if (ftdi->type == TYPE_2232H) + eeprom->suspend_dbus7 = buf[0x01] & SUSPEND_DBUS7_BIT; + + eeprom->chip = buf[0x18]; + eeprom->group0_drive = buf[0x0c] & DRIVE_16MA; + eeprom->group0_schmitt = buf[0x0c] & IS_SCHMITT; + eeprom->group0_slew = buf[0x0c] & SLOW_SLEW; + eeprom->group1_drive = (buf[0x0c] >> 4) & 0x3; + eeprom->group1_schmitt = (buf[0x0c] >> 4) & IS_SCHMITT; + eeprom->group1_slew = (buf[0x0c] >> 4) & SLOW_SLEW; + eeprom->group2_drive = buf[0x0d] & DRIVE_16MA; + eeprom->group2_schmitt = buf[0x0d] & IS_SCHMITT; + eeprom->group2_slew = buf[0x0d] & SLOW_SLEW; + eeprom->group3_drive = (buf[0x0d] >> 4) & DRIVE_16MA; + eeprom->group3_schmitt = (buf[0x0d] >> 4) & IS_SCHMITT; + eeprom->group3_slew = (buf[0x0d] >> 4) & SLOW_SLEW; + } + + if (verbose) + { + char *channel_mode[] = {"UART","245","CPU", "unknown", "OPTO"}; + fprintf(stdout, "VID: 0x%04x\n",eeprom->vendor_id); + fprintf(stdout, "PID: 0x%04x\n",eeprom->product_id); + fprintf(stdout, "Release: 0x%04x\n",release); + + if (eeprom->self_powered) + fprintf(stdout, "Self-Powered%s", (eeprom->remote_wakeup)?", USB Remote Wake Up\n":"\n"); + else + fprintf(stdout, "Bus Powered: %3d mA%s", eeprom->max_power * 2, + (eeprom->remote_wakeup)?" USB Remote Wake Up\n":"\n"); + if (eeprom->manufacturer) + fprintf(stdout, "Manufacturer: %s\n",eeprom->manufacturer); + if (eeprom->product) + fprintf(stdout, "Product: %s\n",eeprom->product); + if (eeprom->serial) + fprintf(stdout, "Serial: %s\n",eeprom->serial); + fprintf(stdout, "Checksum : %04x\n", checksum); + if (ftdi->type == TYPE_R) + fprintf(stdout, "Internal EEPROM\n"); + else if (eeprom->chip >= 0x46) + fprintf(stdout, "Attached EEPROM: 93x%02x\n", eeprom->chip); + if (eeprom->suspend_dbus7) + fprintf(stdout, "Suspend on DBUS7\n"); + if (eeprom->suspend_pull_downs) + fprintf(stdout, "Pull IO pins low during suspend\n"); + if (eeprom->remote_wakeup) + fprintf(stdout, "Enable Remote Wake Up\n"); + fprintf(stdout, "PNP: %d\n",(eeprom->is_not_pnp)?0:1); + if (ftdi->type >= TYPE_2232C) + fprintf(stdout,"Channel A has Mode %s%s%s\n", + channel_mode[eeprom->channel_a_type], + (eeprom->channel_a_driver)?" VCP":"", + (eeprom->high_current_a)?" High Current IO":""); + if ((ftdi->type >= TYPE_2232C) && (ftdi->type != TYPE_R)) + fprintf(stdout,"Channel B has Mode %s%s%s\n", + channel_mode[eeprom->channel_b_type], + (eeprom->channel_b_driver)?" VCP":"", + (eeprom->high_current_b)?" High Current IO":""); + if (((ftdi->type == TYPE_BM) || (ftdi->type == TYPE_2232C)) && + eeprom->use_usb_version == USE_USB_VERSION_BIT) + fprintf(stdout,"Use explicit USB Version %04x\n",eeprom->usb_version); + + if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H)) + { + fprintf(stdout,"%s has %d mA drive%s%s\n", + (ftdi->type == TYPE_2232H)?"AL":"A", + (eeprom->group0_drive+1) *4, + (eeprom->group0_schmitt)?" Schmitt Input":"", + (eeprom->group0_slew)?" Slow Slew":""); + fprintf(stdout,"%s has %d mA drive%s%s\n", + (ftdi->type == TYPE_2232H)?"AH":"B", + (eeprom->group1_drive+1) *4, + (eeprom->group1_schmitt)?" Schmitt Input":"", + (eeprom->group1_slew)?" Slow Slew":""); + fprintf(stdout,"%s has %d mA drive%s%s\n", + (ftdi->type == TYPE_2232H)?"BL":"C", + (eeprom->group2_drive+1) *4, + (eeprom->group2_schmitt)?" Schmitt Input":"", + (eeprom->group2_slew)?" Slow Slew":""); + fprintf(stdout,"%s has %d mA drive%s%s\n", + (ftdi->type == TYPE_2232H)?"BH":"D", + (eeprom->group3_drive+1) *4, + (eeprom->group3_schmitt)?" Schmitt Input":"", + (eeprom->group3_slew)?" Slow Slew":""); + } + if (ftdi->type == TYPE_R) + { + char *cbus_mux[] = {"TXDEN","PWREN","RXLED", "TXLED","TX+RXLED", + "SLEEP","CLK48","CLK24","CLK12","CLK6", + "IOMODE","BB_WR","BB_RD" + }; + char *cbus_BB[] = {"RXF","TXE","RD", "WR"}; + + if (eeprom->invert) + { + char *r_bits[] = {"TXD","RXD","RTS", "CTS","DTR","DSR","DCD","RI"}; + fprintf(stdout,"Inverted bits:"); + for (i=0; i<8; i++) + if ((eeprom->invert & (1<cbus_function[i]cbus_function[i]]); + else + { + /* FIXME for Uwe: This results in an access above array bounds. + Also I couldn't find documentation about this mode. + fprintf(stdout,"C%d BB Function: %s\n", i, + cbus_BB[i]); + */ + fprintf(stdout, "Unknown CBUS mode. Might be special mode?\n"); + (void)cbus_BB; + } + } + } + } + return 0; +} + +/** + Get a value from the decoded EEPROM structure + + \param ftdi pointer to ftdi_context + \param value_name Enum of the value to query + \param value Pointer to store read value + + \retval 0: all fine + \retval -1: Value doesn't exist +*/ +int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int* value) +{ + switch (value_name) + { + case VENDOR_ID: + *value = ftdi->eeprom->vendor_id; + break; + case PRODUCT_ID: + *value = ftdi->eeprom->product_id; + break; + case SELF_POWERED: + *value = ftdi->eeprom->self_powered; + break; + case REMOTE_WAKEUP: + *value = ftdi->eeprom->remote_wakeup; + break; + case IS_NOT_PNP: + *value = ftdi->eeprom->is_not_pnp; + break; + case SUSPEND_DBUS7: + *value = ftdi->eeprom->suspend_dbus7; + break; + case IN_IS_ISOCHRONOUS: + *value = ftdi->eeprom->in_is_isochronous; + break; + case SUSPEND_PULL_DOWNS: + *value = ftdi->eeprom->suspend_pull_downs; + break; + case USE_SERIAL: + *value = ftdi->eeprom->use_serial; + break; + case USB_VERSION: + *value = ftdi->eeprom->usb_version; + break; + case MAX_POWER: + *value = ftdi->eeprom->max_power; + break; + case CHANNEL_A_TYPE: + *value = ftdi->eeprom->channel_a_type; + break; + case CHANNEL_B_TYPE: + *value = ftdi->eeprom->channel_b_type; + break; + case CHANNEL_A_DRIVER: + *value = ftdi->eeprom->channel_a_driver; + break; + case CHANNEL_B_DRIVER: + *value = ftdi->eeprom->channel_b_driver; + break; + case CBUS_FUNCTION_0: + *value = ftdi->eeprom->cbus_function[0]; + break; + case CBUS_FUNCTION_1: + *value = ftdi->eeprom->cbus_function[1]; + break; + case CBUS_FUNCTION_2: + *value = ftdi->eeprom->cbus_function[2]; + break; + case CBUS_FUNCTION_3: + *value = ftdi->eeprom->cbus_function[3]; + break; + case CBUS_FUNCTION_4: + *value = ftdi->eeprom->cbus_function[4]; + break; + case HIGH_CURRENT: + *value = ftdi->eeprom->high_current; + break; + case HIGH_CURRENT_A: + *value = ftdi->eeprom->high_current_a; + break; + case HIGH_CURRENT_B: + *value = ftdi->eeprom->high_current_b; + break; + case INVERT: + *value = ftdi->eeprom->invert; + break; + case GROUP0_DRIVE: + *value = ftdi->eeprom->group0_drive; + break; + case GROUP0_SCHMITT: + *value = ftdi->eeprom->group0_schmitt; + break; + case GROUP0_SLEW: + *value = ftdi->eeprom->group0_slew; + break; + case GROUP1_DRIVE: + *value = ftdi->eeprom->group1_drive; + break; + case GROUP1_SCHMITT: + *value = ftdi->eeprom->group1_schmitt; + break; + case GROUP1_SLEW: + *value = ftdi->eeprom->group1_slew; + break; + case GROUP2_DRIVE: + *value = ftdi->eeprom->group2_drive; + break; + case GROUP2_SCHMITT: + *value = ftdi->eeprom->group2_schmitt; + break; + case GROUP2_SLEW: + *value = ftdi->eeprom->group2_slew; + break; + case GROUP3_DRIVE: + *value = ftdi->eeprom->group3_drive; + break; + case GROUP3_SCHMITT: + *value = ftdi->eeprom->group3_schmitt; + break; + case GROUP3_SLEW: + *value = ftdi->eeprom->group3_slew; + break; + case CHIP_TYPE: + *value = ftdi->eeprom->chip; + break; + case CHIP_SIZE: + *value = ftdi->eeprom->size; + break; + default: + ftdi_error_return(-1, "Request for unknown EEPROM value"); } + return 0; +} + +/** + Set a value in the decoded EEPROM Structure + No parameter checking is performed + + \param ftdi pointer to ftdi_context + \param value_name Enum of the value to set + \param value to set + + \retval 0: all fine + \retval -1: Value doesn't exist + \retval -2: Value not user settable +*/ +int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int value) +{ + switch (value_name) + { + case VENDOR_ID: + ftdi->eeprom->vendor_id = value; + break; + case PRODUCT_ID: + ftdi->eeprom->product_id = value; + break; + case SELF_POWERED: + ftdi->eeprom->self_powered = value; + break; + case REMOTE_WAKEUP: + ftdi->eeprom->remote_wakeup = value; + break; + case IS_NOT_PNP: + ftdi->eeprom->is_not_pnp = value; + break; + case SUSPEND_DBUS7: + ftdi->eeprom->suspend_dbus7 = value; + break; + case IN_IS_ISOCHRONOUS: + ftdi->eeprom->in_is_isochronous = value; + break; + case SUSPEND_PULL_DOWNS: + ftdi->eeprom->suspend_pull_downs = value; + break; + case USE_SERIAL: + ftdi->eeprom->use_serial = value; + break; + case USB_VERSION: + ftdi->eeprom->usb_version = value; + break; + case MAX_POWER: + ftdi->eeprom->max_power = value; + break; + case CHANNEL_A_TYPE: + ftdi->eeprom->channel_a_type = value; + break; + case CHANNEL_B_TYPE: + ftdi->eeprom->channel_b_type = value; + break; + case CHANNEL_A_DRIVER: + ftdi->eeprom->channel_a_driver = value; + break; + case CHANNEL_B_DRIVER: + ftdi->eeprom->channel_b_driver = value; + break; + case CBUS_FUNCTION_0: + ftdi->eeprom->cbus_function[0] = value; + break; + case CBUS_FUNCTION_1: + ftdi->eeprom->cbus_function[1] = value; + break; + case CBUS_FUNCTION_2: + ftdi->eeprom->cbus_function[2] = value; + break; + case CBUS_FUNCTION_3: + ftdi->eeprom->cbus_function[3] = value; + break; + case CBUS_FUNCTION_4: + ftdi->eeprom->cbus_function[4] = value; + break; + case HIGH_CURRENT: + ftdi->eeprom->high_current = value; + break; + case HIGH_CURRENT_A: + ftdi->eeprom->high_current_a = value; + break; + case HIGH_CURRENT_B: + ftdi->eeprom->high_current_b = value; + break; + case INVERT: + ftdi->eeprom->invert = value; + break; + case GROUP0_DRIVE: + ftdi->eeprom->group0_drive = value; + break; + case GROUP0_SCHMITT: + ftdi->eeprom->group0_schmitt = value; + break; + case GROUP0_SLEW: + ftdi->eeprom->group0_slew = value; + break; + case GROUP1_DRIVE: + ftdi->eeprom->group1_drive = value; + break; + case GROUP1_SCHMITT: + ftdi->eeprom->group1_schmitt = value; + break; + case GROUP1_SLEW: + ftdi->eeprom->group1_slew = value; + break; + case GROUP2_DRIVE: + ftdi->eeprom->group2_drive = value; + break; + case GROUP2_SCHMITT: + ftdi->eeprom->group2_schmitt = value; + break; + case GROUP2_SLEW: + ftdi->eeprom->group2_slew = value; + break; + case GROUP3_DRIVE: + ftdi->eeprom->group3_drive = value; + break; + case GROUP3_SCHMITT: + ftdi->eeprom->group3_schmitt = value; + break; + case GROUP3_SLEW: + ftdi->eeprom->group3_slew = value; + break; + case CHIP_TYPE: + ftdi->eeprom->chip = value; + break; + case CHIP_SIZE: + ftdi_error_return(-2, "EEPROM Value can't be changed"); + default : + ftdi_error_return(-1, "Request to unknown EEPROM value"); + } + return 0; +} + +/** Get the read-only buffer to the binary EEPROM content + + \param ftdi pointer to ftdi_context + \param buf buffer to receive EEPROM content + \param size Size of receiving buffer + + \retval 0: All fine + \retval -1: struct ftdi_contxt or ftdi_eeprom missing + \retval -2: Not enough room to store eeprom +*/ +int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size) +{ + if (!ftdi || !(ftdi->eeprom)) + ftdi_error_return(-1, "No appropriate structure"); + + if (!buf || size < ftdi->eeprom->size) + ftdi_error_return(-1, "Not enough room to store eeprom"); + + // Only copy up to FTDI_MAX_EEPROM_SIZE bytes + if (size > FTDI_MAX_EEPROM_SIZE) + size = FTDI_MAX_EEPROM_SIZE; + + memcpy(buf, ftdi->eeprom->buf, size); return 0; } @@ -2656,25 +3297,40 @@ int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsig Read eeprom \param ftdi pointer to ftdi_context - \param eeprom Pointer to store eeprom into \retval 0: all fine \retval -1: read failed \retval -2: USB device unavailable */ -int ftdi_read_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom) +int ftdi_read_eeprom(struct ftdi_context *ftdi) { int i; + unsigned char *buf; if (ftdi == NULL || ftdi->usb_dev == NULL) ftdi_error_return(-2, "USB device unavailable"); + buf = ftdi->eeprom->buf; - for (i = 0; i < ftdi->eeprom_size/2; i++) + for (i = 0; i < FTDI_MAX_EEPROM_SIZE/2; i++) { - if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_EEPROM_REQUEST, 0, i, eeprom+(i*2), 2, ftdi->usb_read_timeout) != 2) + if (libusb_control_transfer( + ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE,SIO_READ_EEPROM_REQUEST, 0, i, + buf+(i*2), 2, ftdi->usb_read_timeout) != 2) ftdi_error_return(-1, "reading eeprom failed"); } + if (ftdi->type == TYPE_R) + ftdi->eeprom->size = 0x80; + /* Guesses size of eeprom by comparing halves + - will not work with blank eeprom */ + else if (strrchr((const char *)buf, 0xff) == ((const char *)buf +FTDI_MAX_EEPROM_SIZE -1)) + ftdi->eeprom->size = -1; + else if (memcmp(buf,&buf[0x80],0x80) == 0) + ftdi->eeprom->size = 0x80; + else if (memcmp(buf,&buf[0x40],0x40) == 0) + ftdi->eeprom->size = 0x40; + else + ftdi->eeprom->size = 0x100; return 0; } @@ -2730,61 +3386,57 @@ int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid) } /** - Guesses size of eeprom by reading eeprom and comparing halves - will not work with blank eeprom - Call this function then do a write then call again to see if size changes, if so write again. + Write eeprom location \param ftdi pointer to ftdi_context - \param eeprom Pointer to store eeprom into - \param maxsize the size of the buffer to read into + \param eeprom_addr Address of eeprom location to be written + \param eeprom_val Value to be written - \retval -1: eeprom read failed + \retval 0: all fine + \retval -1: write failed \retval -2: USB device unavailable - \retval >=0: size of eeprom + \retval -3: Invalid access to checksum protected area below 0x80 + \retval -4: Device can't access unprotected area + \retval -5: Reading chip type failed */ -int ftdi_read_eeprom_getsize(struct ftdi_context *ftdi, unsigned char *eeprom, int maxsize) +int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, + unsigned short eeprom_val) { - int i=0,j,minsize=32; - int size=minsize; + int chip_type_location; + unsigned short chip_type; if (ftdi == NULL || ftdi->usb_dev == NULL) ftdi_error_return(-2, "USB device unavailable"); - do - { - for (j = 0; i < maxsize/2 && jusb_dev, FTDI_DEVICE_IN_REQTYPE, - SIO_READ_EEPROM_REQUEST, 0, i, - eeprom+(i*2), 2, ftdi->usb_read_timeout) != 2) - ftdi_error_return(-1, "eeprom read failed"); - i++; - } - size*=2; - } - while (size<=maxsize && memcmp(eeprom,&eeprom[size/2],size/2)!=0); + if (eeprom_addr <0x80) + ftdi_error_return(-2, "Invalid access to checksum protected area below 0x80"); - return size/2; -} -/** - Write eeprom location - - \param ftdi pointer to ftdi_context - \param eeprom_addr Address of eeprom location to be written - \param eeprom_val Value to be written + switch (ftdi->type) + { + case TYPE_BM: + case TYPE_2232C: + chip_type_location = 0x14; + break; + case TYPE_2232H: + case TYPE_4232H: + chip_type_location = 0x18; + break; + default: + ftdi_error_return(-4, "Device can't access unprotected area"); + } - \retval 0: all fine - \retval -1: read failed - \retval -2: USB device unavailable -*/ -int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, unsigned short eeprom_val) -{ - if (ftdi == NULL || ftdi->usb_dev == NULL) - ftdi_error_return(-2, "USB device unavailable"); + if (ftdi_read_eeprom_location( ftdi, chip_type_location>>1, &chip_type)) + ftdi_error_return(-5, "Reading failed failed"); + fprintf(stderr," loc 0x%04x val 0x%04x\n", chip_type_location,chip_type); + if ((chip_type & 0xff) != 0x66) + { + ftdi_error_return(-6, "EEPROM is not of 93x66"); + } if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, - SIO_WRITE_EEPROM_REQUEST, eeprom_val, eeprom_addr, - NULL, 0, ftdi->usb_write_timeout) != 0) + SIO_WRITE_EEPROM_REQUEST, eeprom_val, eeprom_addr, + NULL, 0, ftdi->usb_write_timeout) != 0) ftdi_error_return(-1, "unable to write eeprom"); return 0; @@ -2794,19 +3446,20 @@ int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, unsig Write eeprom \param ftdi pointer to ftdi_context - \param eeprom Pointer to read eeprom from \retval 0: all fine \retval -1: read failed \retval -2: USB device unavailable */ -int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom) +int ftdi_write_eeprom(struct ftdi_context *ftdi) { unsigned short usb_val, status; int i, ret; + unsigned char *eeprom; if (ftdi == NULL || ftdi->usb_dev == NULL) ftdi_error_return(-2, "USB device unavailable"); + eeprom = ftdi->eeprom->buf; /* These commands were traced while running MProg */ if ((ret = ftdi_usb_reset(ftdi)) != 0) @@ -2816,7 +3469,7 @@ int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom) if ((ret = ftdi_set_latency_timer(ftdi, 0x77)) != 0) return ret; - for (i = 0; i < ftdi->eeprom_size/2; i++) + for (i = 0; i < ftdi->eeprom->size/2; i++) { usb_val = eeprom[i*2]; usb_val += eeprom[(i*2)+1] << 8; @@ -2839,15 +3492,63 @@ int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom) \retval 0: all fine \retval -1: erase failed \retval -2: USB device unavailable + \retval -3: Writing magic failed + \retval -4: Read EEPROM failed + \retval -5: Unexpected EEPROM value */ +#define MAGIC 0x55aa int ftdi_erase_eeprom(struct ftdi_context *ftdi) { + unsigned short eeprom_value; if (ftdi == NULL || ftdi->usb_dev == NULL) ftdi_error_return(-2, "USB device unavailable"); - if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_ERASE_EEPROM_REQUEST, 0, 0, NULL, 0, ftdi->usb_write_timeout) < 0) + if (ftdi->type == TYPE_R) + { + ftdi->eeprom->chip = 0; + return 0; + } + + if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_ERASE_EEPROM_REQUEST, + 0, 0, NULL, 0, ftdi->usb_write_timeout) < 0) ftdi_error_return(-1, "unable to erase eeprom"); + + /* detect chip type by writing 0x55AA as magic at word position 0xc0 + Chip is 93x46 if magic is read at word position 0x00, as wraparound happens around 0x40 + Chip is 93x56 if magic is read at word position 0x40, as wraparound happens around 0x80 + Chip is 93x66 if magic is only read at word position 0xc0*/ + if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, + SIO_WRITE_EEPROM_REQUEST, MAGIC, 0xc0, + NULL, 0, ftdi->usb_write_timeout) != 0) + ftdi_error_return(-3, "Writing magic failed"); + if (ftdi_read_eeprom_location( ftdi, 0x00, &eeprom_value)) + ftdi_error_return(-4, "Reading failed failed"); + if (eeprom_value == MAGIC) + { + ftdi->eeprom->chip = 0x46; + } + else + { + if (ftdi_read_eeprom_location( ftdi, 0x40, &eeprom_value)) + ftdi_error_return(-4, "Reading failed failed"); + if (eeprom_value == MAGIC) + ftdi->eeprom->chip = 0x56; + else + { + if (ftdi_read_eeprom_location( ftdi, 0xc0, &eeprom_value)) + ftdi_error_return(-4, "Reading failed failed"); + if (eeprom_value == MAGIC) + ftdi->eeprom->chip = 0x66; + else + { + ftdi->eeprom->chip = -1; + } + } + } + if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_ERASE_EEPROM_REQUEST, + 0, 0, NULL, 0, ftdi->usb_write_timeout) < 0) + ftdi_error_return(-1, "unable to erase eeprom"); return 0; } diff --git a/src/ftdi.h b/src/ftdi.h index 86e4c62..b7c0113 100644 --- a/src/ftdi.h +++ b/src/ftdi.h @@ -2,7 +2,7 @@ ftdi.h - description ------------------- begin : Fri Apr 4 2003 - copyright : (C) 2003 by Intra2net AG + copyright : (C) 2003-2011 by Intra2net AG and the libftdi developers email : opensource@intra2net.com ***************************************************************************/ @@ -19,7 +19,8 @@ #include -#define FTDI_DEFAULT_EEPROM_SIZE 128 +/* Even on 93xx66 at max 256 bytes are used (AN_121)*/ +#define FTDI_MAX_EEPROM_SIZE 256 /** FTDI chip type */ enum ftdi_chip_type { TYPE_AM=0, TYPE_BM=1, TYPE_2232C=2, TYPE_R=3, TYPE_2232H=4, TYPE_4232H=5 }; @@ -168,6 +169,96 @@ struct ftdi_transfer_control }; /** + \brief FTDI eeprom structure +*/ +struct ftdi_eeprom +{ + /** vendor id */ + int vendor_id; + /** product id */ + int product_id; + + /** self powered */ + int self_powered; + /** remote wakeup */ + int remote_wakeup; + + int is_not_pnp; + + /* Suspend on DBUS7 Low */ + int suspend_dbus7; + + /** input in isochronous transfer mode */ + int in_is_isochronous; + /** output in isochronous transfer mode */ + int out_is_isochronous; + /** suspend pull downs */ + int suspend_pull_downs; + + /** use serial */ + int use_serial; + /** usb version */ + int usb_version; + /** Use usb version on FT2232 devices*/ + int use_usb_version; + /** maximum power */ + int max_power; + + /** manufacturer name */ + char *manufacturer; + /** product name */ + char *product; + /** serial number */ + char *serial; + + /* 2232D/H(/FT4432H?) specific */ + /* Hardware type, 0 = RS232 Uart, 1 = 245 FIFO, 2 = CPU FIFO, + 4 = OPTO Isolate */ + int channel_a_type; + int channel_b_type; + /* Driver Type, 1 = VCP */ + int channel_a_driver; + int channel_b_driver; + + /* Special function of FT232R devices (and possibly others as well) */ + /** CBUS pin function. See CBUS_xxx defines. */ + int cbus_function[5]; + /** Select hight current drive on R devices. */ + int high_current; + /** Select hight current drive on A channel (2232C */ + int high_current_a; + /** Select hight current drive on B channel (2232C). */ + int high_current_b; + /** Select inversion of data lines (bitmask). */ + int invert; + + /*2232H/4432H Group specific values */ + /* Group0 is AL on 2322H and A on 4232H + Group1 is AH on 2232H and B on 4232H + Group2 is BL on 2322H and C on 4232H + Group3 is BH on 2232H and C on 4232H*/ + int group0_drive; + int group0_schmitt; + int group0_slew; + int group1_drive; + int group1_schmitt; + int group1_slew; + int group2_drive; + int group2_schmitt; + int group2_slew; + int group3_drive; + int group3_schmitt; + int group3_slew; + + /** eeprom size in bytes. This doesn't get stored in the eeprom + but is the only way to pass it to ftdi_eeprom_build. */ + int size; + /* EEPROM Type 0x46 for 93xx46, 0x56 for 93xx56 and 0x66 for 93xx66*/ + int chip; + unsigned char buf[FTDI_MAX_EEPROM_SIZE]; +}; + +/** \brief Main context structure for all libftdi functions. Do not access directly if possible. @@ -217,14 +308,61 @@ struct ftdi_context /** Bitbang mode. 1: (default) Normal bitbang mode, 2: FT2232C SPI bitbang mode */ unsigned char bitbang_mode; - /** EEPROM size. Default is 128 bytes for 232BM and 245BM chips */ - int eeprom_size; + /** Decoded eeprom structure */ + struct ftdi_eeprom *eeprom; /** String representation of last error */ char *error_str; }; /** + List all handled EEPROM values. + Append future new values only at the end to provide API/ABI stability*/ +enum ftdi_eeprom_value +{ + VENDOR_ID = 0, + PRODUCT_ID = 1, + SELF_POWERED = 2, + REMOTE_WAKEUP = 3, + IS_NOT_PNP = 4, + SUSPEND_DBUS7 = 5, + IN_IS_ISOCHRONOUS = 6, + OUT_IS_ISOCHRONOUS = 7, + SUSPEND_PULL_DOWNS = 8, + USE_SERIAL = 9, + USB_VERSION = 10, + USE_USB_VERSION = 11, + MAX_POWER = 12, + CHANNEL_A_TYPE = 13, + CHANNEL_B_TYPE = 14, + CHANNEL_A_DRIVER = 15, + CHANNEL_B_DRIVER = 16, + CBUS_FUNCTION_0 = 17, + CBUS_FUNCTION_1 = 18, + CBUS_FUNCTION_2 = 19, + CBUS_FUNCTION_3 = 20, + CBUS_FUNCTION_4 = 21, + HIGH_CURRENT = 22, + HIGH_CURRENT_A = 23, + HIGH_CURRENT_B = 24, + INVERT = 25, + GROUP0_DRIVE = 26, + GROUP0_SCHMITT = 27, + GROUP0_SLEW = 28, + GROUP1_DRIVE = 29, + GROUP1_SCHMITT = 30, + GROUP1_SLEW = 31, + GROUP2_DRIVE = 32, + GROUP2_SCHMITT = 33, + GROUP2_SLEW = 34, + GROUP3_DRIVE = 35, + GROUP3_SCHMITT = 36, + GROUP3_SLEW = 37, + CHIP_SIZE = 38, + CHIP_TYPE = 39 +}; + +/** \brief list of usb devices created by ftdi_usb_find_all() */ struct ftdi_device_list @@ -235,33 +373,11 @@ struct ftdi_device_list struct libusb_device *dev; }; -/** TXDEN */ -#define CBUS_TXDEN 0 -/** PWREN# */ -#define CBUS_PWREN 1 -/** RXLED# */ -#define CBUS_RXLED 2 -/** TXLED#*/ -#define CBUS_TXLED 3 -/** RXLED# & TXLED# */ -#define CBUS_TXRXLED 4 -/** SLEEP# */ -#define CBUS_SLEEP 5 -/** 48 MHz clock */ -#define CBUS_CLK48 6 -/** 24 MHz clock */ -#define CBUS_CLK24 7 -/** 12 MHz clock */ -#define CBUS_CLK12 8 -/** 6 MHz clock */ -#define CBUS_CLK6 9 -/** Bitbang IO Mode*/ -#define CBUS_IOMODE 10 -/** Bitbang IO WR#*/ -#define CBUS_BB_WR 11 -/** Bitbang IO RD#*/ -#define CBUS_BB_RD 12 - +#define USE_SERIAL_NUM 0x08 +enum ftdi_cbus_func {/* FIXME: Recheck value, especially the last */ + CBUS_TXDEN = 0, CBUS_PWREN = 1, CBUS_RXLED = 2, CBUS_TXLED = 3, CBUS_TXRXLED = 4, + CBUS_SLEEP = 5, CBUS_CLK48 = 6, CBUS_CLK24 = 7, CBUS_CLK12 = 8, CBUS_CLK6 = 9, + CBUS_IOMODE = 0xa, CBUS_BB_WR = 0xb, CBUS_BB_RD = 0xc, CBUS_BB = 0xd}; /** Invert TXD# */ #define INVERT_TXD 0x01 @@ -280,61 +396,29 @@ struct ftdi_device_list /** Invert RI# */ #define INVERT_RI 0x80 -/** High current drive. */ -#define HIGH_CURRENT_DRIVE 0x04 +/** Interface Mode. */ +#define CHANNEL_IS_UART 0x0 +#define CHANNEL_IS_245 0x1 +#define CHANNEL_IS_CPU 0x2 +#define CHANNEL_IS_OPTO 0x4 -/** - \brief FTDI eeprom structure -*/ -struct ftdi_eeprom -{ - /** vendor id */ - int vendor_id; - /** product id */ - int product_id; - - /** self powered */ - int self_powered; - /** remote wakeup */ - int remote_wakeup; - /** chip type */ - int chip_type; - - /** input in isochronous transfer mode */ - int in_is_isochronous; - /** output in isochronous transfer mode */ - int out_is_isochronous; - /** suspend pull downs */ - int suspend_pull_downs; +#define DRIVE_4MA 0 +#define DRIVE_8MA 1 +#define DRIVE_12MA 2 +#define DRIVE_16MA 3 +#define SLOW_SLEW 4 +#define IS_SCHMITT 8 - /** use serial */ - int use_serial; - /** fake usb version */ - int change_usb_version; - /** usb version */ - int usb_version; - /** maximum power */ - int max_power; +/** Driver Type. */ +#define DRIVER_VCP 0x08 - /** manufacturer name */ - char *manufacturer; - /** product name */ - char *product; - /** serial number */ - char *serial; +#define USE_USB_VERSION_BIT 0x10 - /* Special function of FT232R devices (and possibly others as well) */ - /** CBUS pin function. See CBUS_xxx defines. */ - int cbus_function[5]; - /** Select hight current drive. */ - int high_current; - /** Select inversion of data lines (bitmask). */ - int invert; +#define SUSPEND_DBUS7_BIT 0x80 - /** eeprom size in bytes. This doesn't get stored in the eeprom - but is the only way to pass it to ftdi_eeprom_build. */ - int size; -}; +/** High current drive. */ +#define HIGH_CURRENT_DRIVE 0x10 +#define HIGH_CURRENT_DRIVE_R 0x04 /** \brief Progress Info for streaming read @@ -437,21 +521,21 @@ extern "C" int ftdi_set_event_char(struct ftdi_context *ftdi, unsigned char eventch, unsigned char enable); int ftdi_set_error_char(struct ftdi_context *ftdi, unsigned char errorch, unsigned char enable); - /* set eeprom size */ - void ftdi_eeprom_setsize(struct ftdi_context *ftdi, struct ftdi_eeprom *eeprom, int size); + /* init eeprom for the given FTDI type */ + int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, + char * manufacturer, char *product, + char * serial); + int ftdi_eeprom_build(struct ftdi_context *ftdi); + int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose); + + int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int* value); + int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int value); - /* init and build eeprom from ftdi_eeprom structure */ - void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom); - void ftdi_eeprom_free(struct ftdi_eeprom *eeprom); - int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output); - int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *output, int size); + int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size); - /* "eeprom" needs to be valid 128 byte eeprom (generated by the eeprom generator) - the checksum of the eeprom is valided */ - int ftdi_read_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom); + int ftdi_read_eeprom(struct ftdi_context *ftdi); int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid); - int ftdi_read_eeprom_getsize(struct ftdi_context *ftdi, unsigned char *eeprom, int maxsize); - int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom); + int ftdi_write_eeprom(struct ftdi_context *ftdi); int ftdi_erase_eeprom(struct ftdi_context *ftdi); int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsigned short *eeprom_val);