Merge branch 'eeprom-new'
authorThomas Jarosch <thomas.jarosch@intra2net.com>
Fri, 20 May 2011 14:10:11 +0000 (16:10 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Fri, 20 May 2011 14:10:11 +0000 (16:10 +0200)
Conflicts:
examples/stream_test.c

19 files changed:
.gitignore
CMakeLists.txt
bindings/ftdi.i
doc/EEPROM-structure [new file with mode: 0644]
examples/CMakeLists.txt
examples/baud_test.c
examples/eeprom.c [new file with mode: 0644]
examples/serial_read.c
ftdi_eeprom/CMakeLists.txt [new file with mode: 0644]
ftdi_eeprom/example.conf [new file with mode: 0644]
ftdi_eeprom/ftdi_eeprom_version.h.in [new file with mode: 0644]
ftdi_eeprom/main.c [new file with mode: 0644]
ftdipp/ftdi.cpp
ftdipp/ftdi.hpp
libftdi-1.0.kdev4 [new file with mode: 0644]
libftdi.kdevelop [deleted file]
libftdi.lnt
src/ftdi.c
src/ftdi.h

index 4bacdb7..5a47eba 100644 (file)
@@ -7,6 +7,7 @@
 *.pc
 .deps/
 .libs/
+.kdev4/
 build/
 
 # Automake
index a661d0f..73517cf 100644 (file)
@@ -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)
 
index ce1149c..b1d550a 100644 (file)
@@ -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 (file)
index 0000000..c83337e
--- /dev/null
@@ -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."
index af7f57a..d8be6f5 100644 (file)
@@ -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)
index b53031b..d8d4886 100644 (file)
@@ -12,7 +12,7 @@
  *       s:<vendor>:<product>:<serial> first device with given vendor id, product id and serial string
  *  -d <datasize to send in bytes>
  *  -b <baudrate> (divides by 16 if bitbang as taken from the ftdi datasheets)
- *  -m <mode to use> r: serial a: async bitbang s:sync bitbang 
+ *  -m <mode to use> r: serial a: async bitbang s:sync bitbang
  *  -c <chunksize>
  *
  * (C) 2009 by Gerd v. Egidy <gerd.von.egidy@intra2net.com>
@@ -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 (file)
index 0000000..e2c8052
--- /dev/null
@@ -0,0 +1,236 @@
+/* LIBFTDI EEPROM access example
+
+   This program is distributed under the GPL, version 2
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <ftdi.h>
+
+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 <number> Search for device with PID == number\n");
+                fprintf(stderr, "\t-v <number> Search for device with VID == number\n");
+                fprintf(stderr, "\t-P <string? Search for device with given "
+                        "product description\n");
+                fprintf(stderr, "\t-S <string? Search for device with given "
+                        "serial number\n");
+                exit(-1);
+        }
+    }
+
+    // Select first interface
+    ftdi_set_interface(ftdi, INTERFACE_ANY);
+
+    // Open device
+    f = ftdi_usb_open_desc(ftdi, vid, pid, desc, serial);
+    if (f < 0)
+    {
+        fprintf(stderr, "Device VID 0x%04x PID 0x%04x", vid, pid);
+        if (desc)
+            fprintf(stderr, " Desc %s", desc);
+        if (serial)
+            fprintf(stderr, " Serial %s", serial);
+        fprintf(stderr, "\n");
+        fprintf(stderr, "unable to open ftdi device: %d (%s)\n",
+                f, ftdi_get_error_string(ftdi));
+
+        exit(-1);
+    }
+
+    if (erase)
+    {
+        f = ftdi_erase_eeprom(ftdi); /* needed to determine EEPROM chip type */
+        if (f < 0)
+        {
+            fprintf(stderr, "Erase failed: %s",
+                    ftdi_get_error_string(ftdi));
+            return -2;
+        }
+        if (ftdi_get_eeprom_value(ftdi, CHIP_TYPE, & value) <0)
+        {
+            fprintf(stderr, "ftdi_get_eeprom_value: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+        }
+        if (value == -1)
+            fprintf(stderr, "No EEPROM\n");
+        else if (value == 0)
+            fprintf(stderr, "Internal EEPROM\n");
+        else
+            fprintf(stderr, "Found 93x%02x\n", value);
+        return 0;
+    }
+
+    if (use_defaults)
+    {
+        ftdi_eeprom_initdefaults(ftdi, "IKDA", "FTDIJTAG", "0001");
+        ftdi_eeprom_initdefaults(ftdi, "IKDA", "FTDIJTAG", "0001");
+        if (ftdi_set_eeprom_value(ftdi, MAX_POWER, 500) <0)
+        {
+            fprintf(stderr, "ftdi_set_eeprom_value: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+        }
+        if (large_chip)
+            if (ftdi_set_eeprom_value(ftdi, CHIP_TYPE, 0x66) <0)
+            {
+                fprintf(stderr, "ftdi_set_eeprom_value: %d (%s)\n",
+                        f, ftdi_get_error_string(ftdi));
+            }
+        f=(ftdi_eeprom_build(ftdi));
+        if (f < 0)
+        {
+            fprintf(stderr, "ftdi_eeprom_build: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+            exit(-1);
+        }
+    }
+    else if (do_write)
+    {
+        ftdi_eeprom_initdefaults(ftdi, "IKDA", "FTDIJTAG", "0001");
+        f = ftdi_erase_eeprom(ftdi);
+        if (ftdi_set_eeprom_value(ftdi, MAX_POWER, 500) <0)
+        {
+            fprintf(stderr, "ftdi_set_eeprom_value: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+        }
+        f = ftdi_erase_eeprom(ftdi);/* needed to determine EEPROM chip type */
+        if (ftdi_get_eeprom_value(ftdi, CHIP_TYPE, & value) <0)
+        {
+            fprintf(stderr, "ftdi_get_eeprom_value: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+        }
+        if (value == -1)
+            fprintf(stderr, "No EEPROM\n");
+        else if (value == 0)
+            fprintf(stderr, "Internal EEPROM\n");
+        else
+            fprintf(stderr, "Found 93x%02x\n", value);
+        f=(ftdi_eeprom_build(ftdi));
+        if (f < 0)
+        {
+            fprintf(stderr, "Erase failed: %s",
+                    ftdi_get_error_string(ftdi));
+            return -2;
+        }
+        f = ftdi_write_eeprom(ftdi);
+        {
+            fprintf(stderr, "ftdi_eeprom_decode: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+            exit(-1);
+        }
+        f = ftdi_read_eeprom(ftdi);
+        if (f < 0)
+        {
+            fprintf(stderr, "ftdi_read_eeprom: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+            exit(-1);
+        }
+    }
+    else
+    {
+        f = ftdi_read_eeprom(ftdi);
+        if (f < 0)
+        {
+            fprintf(stderr, "ftdi_read_eeprom: %d (%s)\n",
+                    f, ftdi_get_error_string(ftdi));
+            exit(-1);
+        }
+    }
+
+
+    ftdi_get_eeprom_value(ftdi, CHIP_SIZE, & value);
+    fprintf(stderr, "Chip type %d ftdi_eeprom_size: %d\n", ftdi->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;
+}
index 5d05d66..aec18d0 100644 (file)
@@ -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 (file)
index 0000000..3f1d7dc
--- /dev/null
@@ -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 (file)
index 0000000..cfc9fc3
--- /dev/null
@@ -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 (file)
index 0000000..db7717d
--- /dev/null
@@ -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 (file)
index 0000000..3977f86
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <confuse.h>
+#include <ftdi.h>
+#include <ftdi_eeprom_version.h>
+
+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<max_allowed; i++) {
+        if (!(strcmp(options[i], str))) {
+            return i;
+        }
+    }
+    printf("WARNING: Invalid cbus option '%s'\n", str);
+    return 0;
+}
+
+/**
+ * @brief Set eeprom value
+ *
+ * \param ftdi pointer to ftdi_context
+ * \param value_name Enum of the value to set
+ * \param value Value to set
+ *
+ * Function will abort the program on error
+ **/
+static void eeprom_set_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int value)
+{
+    if (ftdi_set_eeprom_value(ftdi, value_name, value) < 0)
+    {
+        printf("Unable to set eeprom value %d: %s. Aborting\n", value_name, ftdi_get_error_string(ftdi));
+        exit (-1);
+    }
+}
+
+/**
+ * @brief Get eeprom value
+ *
+ * \param ftdi pointer to ftdi_context
+ * \param value_name Enum of the value to get
+ * \param value Value to get
+ *
+ * Function will abort the program on error
+ **/
+static void eeprom_get_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int *value)
+{
+    if (ftdi_get_eeprom_value(ftdi, value_name, value) < 0)
+    {
+        printf("Unable to get eeprom value %d: %s. Aborting\n", value_name, ftdi_get_error_string(ftdi));
+        exit (-1);
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    /*
+    configuration options
+    */
+    cfg_opt_t opts[] =
+    {
+        CFG_INT("vendor_id", 0, 0),
+        CFG_INT("product_id", 0, 0),
+        CFG_BOOL("self_powered", cfg_true, 0),
+        CFG_BOOL("remote_wakeup", cfg_true, 0),
+        CFG_STR_LIST("chip_type", "{BM,R,other}", 0),
+        CFG_BOOL("in_is_isochronous", cfg_false, 0),
+        CFG_BOOL("out_is_isochronous", cfg_false, 0),
+        CFG_BOOL("suspend_pull_downs", cfg_false, 0),
+        CFG_BOOL("use_serial", cfg_false, 0),
+        CFG_BOOL("change_usb_version", cfg_false, 0),
+        CFG_INT("usb_version", 0, 0),
+        CFG_INT("default_pid", 0x6001, 0),
+        CFG_INT("max_power", 0, 0),
+        CFG_STR("manufacturer", "Acme Inc.", 0),
+        CFG_STR("product", "USB Serial Converter", 0),
+        CFG_STR("serial", "08-15", 0),
+        CFG_STR("filename", "", 0),
+        CFG_BOOL("flash_raw", cfg_false, 0),
+        CFG_BOOL("high_current", cfg_false, 0),
+        CFG_STR_LIST("cbus0", "{TXDEN,PWREN,RXLED,TXLED,TXRXLED,SLEEP,CLK48,CLK24,CLK12,CLK6,IO_MODE,BITBANG_WR,BITBANG_RD,SPECIAL}", 0),
+        CFG_STR_LIST("cbus1", "{TXDEN,PWREN,RXLED,TXLED,TXRXLED,SLEEP,CLK48,CLK24,CLK12,CLK6,IO_MODE,BITBANG_WR,BITBANG_RD,SPECIAL}", 0),
+        CFG_STR_LIST("cbus2", "{TXDEN,PWREN,RXLED,TXLED,TXRXLED,SLEEP,CLK48,CLK24,CLK12,CLK6,IO_MODE,BITBANG_WR,BITBANG_RD,SPECIAL}", 0),
+        CFG_STR_LIST("cbus3", "{TXDEN,PWREN,RXLED,TXLED,TXRXLED,SLEEP,CLK48,CLK24,CLK12,CLK6,IO_MODE,BITBANG_WR,BITBANG_RD,SPECIAL}", 0),
+        CFG_STR_LIST("cbus4", "{TXDEN,PWRON,RXLED,TXLED,TX_RX_LED,SLEEP,CLK48,CLK24,CLK12,CLK6}", 0),
+        CFG_BOOL("invert_txd", cfg_false, 0),
+        CFG_BOOL("invert_rxd", cfg_false, 0),
+        CFG_BOOL("invert_rts", cfg_false, 0),
+        CFG_BOOL("invert_cts", cfg_false, 0),
+        CFG_BOOL("invert_dtr", cfg_false, 0),
+        CFG_BOOL("invert_dsr", cfg_false, 0),
+        CFG_BOOL("invert_dcd", cfg_false, 0),
+        CFG_BOOL("invert_ri", cfg_false, 0),
+        CFG_END()
+    };
+    cfg_t *cfg;
+
+    /*
+    normal variables
+    */
+    int _read = 0, _erase = 0, _flash = 0;
+
+    const int my_eeprom_size = 128;                 /* TODO: Kill this. Check with Uwe how we can determine the eeprom size properly
+                                                             because it's initialized with -1. Maybe assume 128 bytes per default? */
+    unsigned char eeprom_buf[my_eeprom_size];
+    char *filename;
+    int size_check;
+    int i, argc_filename;
+    FILE *fp;
+
+    struct ftdi_context *ftdi = NULL;
+
+    printf("\nFTDI eeprom generator v%s\n", EEPROM_VERSION_STRING);
+    printf ("(c) Intra2net AG and the libftdi developers <opensource@intra2net.com>\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;
+}
index c81eb5f..2754033 100644 (file)
@@ -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)
index ba20346..3a5d993 100644 (file)
@@ -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 (file)
index 0000000..6da24c2
--- /dev/null
@@ -0,0 +1,3 @@
+[Project]
+Manager=KDevCMakeManager
+Name=libftdi-1.0
diff --git a/libftdi.kdevelop b/libftdi.kdevelop
deleted file mode 100644 (file)
index 421d88d..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-<?xml version = '1.0'?>
-<kdevelop>
-  <general>
-    <author>Thomas Jarosch</author>
-    <email>opensource@intra2net.com</email>
-    <version>0.6</version>
-    <projectmanagement>KDevAutoProject</projectmanagement>
-    <primarylanguage>C</primarylanguage>
-    <keywords>
-      <keyword>C</keyword>
-      <keyword>Code</keyword>
-    </keywords>
-    <projectdirectory>.</projectdirectory>
-    <absoluteprojectpath>false</absoluteprojectpath>
-    <description/>
-    <ignoreparts/>
-    <secondaryLanguages/>
-    <projectname>libftdi</projectname>
-  </general>
-  <kdevautoproject>
-    <general>
-      <activetarget>src/libftdi</activetarget>
-      <useconfiguration>default</useconfiguration>
-    </general>
-    <run>
-      <mainprogram>src/libftdi</mainprogram>
-      <terminal>true</terminal>
-      <directoryradio>executable</directoryradio>
-      <customdirectory>/</customdirectory>
-      <programargs/>
-      <autocompile>true</autocompile>
-      <envvars/>
-    </run>
-    <configurations>
-      <optimized>
-        <builddir>optimized</builddir>
-        <ccompiler>kdevgccoptions</ccompiler>
-        <cxxcompiler>kdevgppoptions</cxxcompiler>
-        <f77compiler>kdevg77options</f77compiler>
-        <cflags>-O2 -g0</cflags>
-      </optimized>
-      <debug>
-        <configargs>--enable-debug=full</configargs>
-        <builddir>debug</builddir>
-        <ccompiler>kdevgccoptions</ccompiler>
-        <cxxcompiler>kdevgppoptions</cxxcompiler>
-        <f77compiler>kdevg77options</f77compiler>
-        <cflags>-O0 -g3</cflags>
-      </debug>
-      <default>
-        <envvars/>
-      </default>
-    </configurations>
-    <make>
-      <envvars>
-        <envvar value="1" name="WANT_AUTOCONF_2_5" />
-        <envvar value="1" name="WANT_AUTOMAKE_1_6" />
-      </envvars>
-      <abortonerror>false</abortonerror>
-      <numberofjobs>1</numberofjobs>
-      <dontact>false</dontact>
-      <makebin/>
-      <prio>0</prio>
-    </make>
-  </kdevautoproject>
-  <kdevdoctreeview>
-    <ignoretocs>
-      <toc>ada</toc>
-      <toc>ada_bugs_gcc</toc>
-      <toc>bash</toc>
-      <toc>bash_bugs</toc>
-      <toc>clanlib</toc>
-      <toc>w3c-dom-level2-html</toc>
-      <toc>fortran_bugs_gcc</toc>
-      <toc>gnome1</toc>
-      <toc>gnustep</toc>
-      <toc>gtk</toc>
-      <toc>gtk_bugs</toc>
-      <toc>haskell</toc>
-      <toc>haskell_bugs_ghc</toc>
-      <toc>java_bugs_gcc</toc>
-      <toc>java_bugs_sun</toc>
-      <toc>kde2book</toc>
-      <toc>libstdc++</toc>
-      <toc>opengl</toc>
-      <toc>pascal_bugs_fp</toc>
-      <toc>php</toc>
-      <toc>php_bugs</toc>
-      <toc>perl</toc>
-      <toc>perl_bugs</toc>
-      <toc>python</toc>
-      <toc>python_bugs</toc>
-      <toc>qt-kdev3</toc>
-      <toc>ruby</toc>
-      <toc>ruby_bugs</toc>
-      <toc>sdl</toc>
-      <toc>stl</toc>
-      <toc>w3c-svg</toc>
-      <toc>sw</toc>
-      <toc>w3c-uaag10</toc>
-      <toc>wxwindows_bugs</toc>
-    </ignoretocs>
-    <ignoreqt_xml>
-      <toc>Guide to the Qt Translation Tools</toc>
-      <toc>Qt Assistant Manual</toc>
-      <toc>Qt Designer Manual</toc>
-      <toc>Qt Reference Documentation</toc>
-      <toc>qmake User Guide</toc>
-    </ignoreqt_xml>
-    <ignoredoxygen>
-      <toc>KDE Libraries (Doxygen)</toc>
-    </ignoredoxygen>
-  </kdevdoctreeview>
-  <kdevfilecreate>
-    <filetypes/>
-    <useglobaltypes>
-      <type ext="c" />
-      <type ext="h" />
-    </useglobaltypes>
-  </kdevfilecreate>
-  <kdevfileview>
-    <groups>
-      <group pattern="*.h" name="Header files" />
-      <group pattern="*.c" name="Source files" />
-      <hidenonprojectfiles>false</hidenonprojectfiles>
-      <hidenonlocation>false</hidenonlocation>
-    </groups>
-    <tree>
-      <hidepatterns>*.o,*.lo,CVS</hidepatterns>
-      <hidenonprojectfiles>false</hidenonprojectfiles>
-      <showvcsfields>false</showvcsfields>
-    </tree>
-  </kdevfileview>
-  <kdevcppsupport>
-    <references/>
-    <codecompletion>
-      <includeGlobalFunctions>true</includeGlobalFunctions>
-      <includeTypes>true</includeTypes>
-      <includeEnums>true</includeEnums>
-      <includeTypedefs>false</includeTypedefs>
-      <automaticCodeCompletion>true</automaticCodeCompletion>
-      <automaticArgumentsHint>true</automaticArgumentsHint>
-      <automaticHeaderCompletion>true</automaticHeaderCompletion>
-      <codeCompletionDelay>250</codeCompletionDelay>
-      <argumentsHintDelay>400</argumentsHintDelay>
-      <headerCompletionDelay>250</headerCompletionDelay>
-      <showOnlyAccessibleItems>false</showOnlyAccessibleItems>
-      <completionBoxItemOrder>0</completionBoxItemOrder>
-      <howEvaluationContextMenu>true</howEvaluationContextMenu>
-      <showCommentWithArgumentHint>true</showCommentWithArgumentHint>
-      <statusBarTypeEvaluation>false</statusBarTypeEvaluation>
-      <namespaceAliases>std=_GLIBCXX_STD;__gnu_cxx=std</namespaceAliases>
-      <processPrimaryTypes>true</processPrimaryTypes>
-      <processFunctionArguments>false</processFunctionArguments>
-      <preProcessAllHeaders>false</preProcessAllHeaders>
-      <parseMissingHeadersExperimental>false</parseMissingHeadersExperimental>
-      <resolveIncludePathsUsingMakeExperimental>false</resolveIncludePathsUsingMakeExperimental>
-      <alwaysParseInBackground>true</alwaysParseInBackground>
-      <usePermanentCaching>true</usePermanentCaching>
-      <alwaysIncludeNamespaces>false</alwaysIncludeNamespaces>
-      <includePaths>.;</includePaths>
-    </codecompletion>
-    <qt>
-      <used>false</used>
-      <version>3</version>
-      <includestyle>3</includestyle>
-      <root>/usr/lib/qt-3.3</root>
-      <designerintegration>EmbeddedKDevDesigner</designerintegration>
-      <qmake>/usr/lib/qt-3.3/bin/qmake</qmake>
-      <designer>/usr/lib/qt-3.3/bin/designer</designer>
-      <designerpluginpaths/>
-    </qt>
-  </kdevcppsupport>
-  <cppsupportpart>
-    <filetemplates>
-      <interfacesuffix>.h</interfacesuffix>
-      <implementationsuffix>.cpp</implementationsuffix>
-    </filetemplates>
-  </cppsupportpart>
-  <kdevdebugger>
-    <general>
-      <programargs/>
-      <gdbpath/>
-      <dbgshell/>
-      <configGdbScript/>
-      <runShellScript/>
-      <runGdbScript/>
-      <breakonloadinglibs>true</breakonloadinglibs>
-      <separatetty>false</separatetty>
-      <floatingtoolbar>false</floatingtoolbar>
-    </general>
-    <display>
-      <staticmembers>false</staticmembers>
-      <demanglenames>true</demanglenames>
-      <outputradix>10</outputradix>
-    </display>
-  </kdevdebugger>
-  <kdevdocumentation>
-    <projectdoc>
-      <docsystem/>
-      <docurl/>
-      <usermanualurl/>
-    </projectdoc>
-  </kdevdocumentation>
-  <kdevcvsservice>
-    <recursivewhenupdate>true</recursivewhenupdate>
-    <prunedirswhenupdate>true</prunedirswhenupdate>
-    <createdirswhenupdate>true</createdirswhenupdate>
-    <recursivewhencommitremove>true</recursivewhencommitremove>
-    <revertoptions>-C</revertoptions>
-  </kdevcvsservice>
-</kdevelop>
index 89ad21d..2682b95 100644 (file)
@@ -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)
index 41496ee..c53d7aa 100644 (file)
@@ -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;j<manufacturer_size-1;j++)
+            {
+                eeprom->manufacturer[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;j<product_size-1;j++)
+            {
+                eeprom->product[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<manufacturer_size-1;j++)
+    if (serial_size > 0)
     {
-        eeprom->manufacturer[j] = buf[2*j+i+2];
-    }
-    eeprom->manufacturer[j] = '\0';
-
-    // Decode product name
-    i = buf[0x10] & 0x7f; // offset
-    for (j=0;j<product_size-1;j++)
-    {
-        eeprom->product[j] = buf[2*j+i+2];
-    }
-    eeprom->product[j] = '\0';
-
-    // Decode serial
-    i = buf[0x12] & 0x7f; // offset
-    for (j=0;j<serial_size-1;j++)
-    {
-        eeprom->serial[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;j<serial_size-1;j++)
+            {
+                eeprom->serial[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<<i)) == (1<<i))
+                        fprintf(stdout," %s",r_bits[i]);
+                fprintf(stdout,"\n");
+            }
+            for (i=0; i<5; i++)
+            {
+                if (eeprom->cbus_function[i]<CBUS_BB)
+                    fprintf(stdout,"C%d Function: %s\n", i,
+                            cbus_mux[eeprom->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 && j<size; j++)
-        {
-            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)
-                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;
 }
 
index 86e4c62..b7c0113 100644 (file)
@@ -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 <libusb.h>
 
-#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);