Use BM/R series baud rate computation for FT230X.
[libftdi] / src / ftdi.c
index c7856a4..0b26a7a 100644 (file)
@@ -2,7 +2,7 @@
                           ftdi.c  -  description
                              -------------------
     begin                : Fri Apr 4 2003
-    copyright            : (C) 2003-2011 by Intra2net AG and the libftdi developers
+    copyright            : (C) 2003-2014 by Intra2net AG and the libftdi developers
     email                : opensource@intra2net.com
  ***************************************************************************/
 
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "ftdi_i.h"
 #include "ftdi.h"
+#include "ftdi_version_i.h"
 
 #define ftdi_error_return(code, str) do {  \
-        ftdi->error_str = str;             \
+        if ( ftdi )                        \
+            ftdi->error_str = str;         \
+        else                               \
+            fprintf(stderr, str);          \
         return code;                       \
    } while(0);
 
@@ -63,6 +68,8 @@ static void ftdi_usb_close_internal (struct ftdi_context *ftdi)
     {
         libusb_close (ftdi->usb_dev);
         ftdi->usb_dev = NULL;
+        if(ftdi->eeprom)
+            ftdi->eeprom->initialized_for_connected_device = 0;
     }
 }
 
@@ -74,6 +81,7 @@ 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
+    \retval -3: libusb_init() failed
 
     \remark This should be called before all functions
 */
@@ -94,12 +102,15 @@ int ftdi_init(struct ftdi_context *ftdi)
     ftdi->readbuffer_remaining = 0;
     ftdi->writebuffer_chunksize = 4096;
     ftdi->max_packet_size = 0;
+    ftdi->error_str = NULL;
+    ftdi->module_detach_mode = AUTO_DETACH_SIO_MODULE;
+
+    if (libusb_init(&ftdi->usb_ctx) < 0)
+        ftdi_error_return(-3, "libusb_init() failed");
 
     ftdi_set_interface(ftdi, INTERFACE_ANY);
     ftdi->bitbang_mode = 1; /* when bitbang is enabled this holds the number of the mode  */
 
-    ftdi->error_str = NULL;
-
     if (eeprom == 0)
         ftdi_error_return(-2, "Can't malloc struct ftdi_eeprom");
     memset(eeprom, 0, sizeof(struct ftdi_eeprom));
@@ -141,12 +152,23 @@ struct ftdi_context *ftdi_new(void)
     \retval  0: all fine
     \retval -1: unknown interface
     \retval -2: USB device unavailable
+    \retval -3: Device already open, interface can't be set in that state
 */
 int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface interface)
 {
     if (ftdi == NULL)
         ftdi_error_return(-2, "USB device unavailable");
 
+    if (ftdi->usb_dev != NULL)
+    {
+        int check_interface = interface;
+        if (check_interface == INTERFACE_ANY)
+            check_interface = INTERFACE_A;
+
+        if (ftdi->index != check_interface)
+            ftdi_error_return(-3, "Interface can not be changed on an already open device");
+    }
+
     switch (interface)
     {
         case INTERFACE_ANY:
@@ -218,7 +240,12 @@ void ftdi_deinit(struct ftdi_context *ftdi)
         free(ftdi->eeprom);
         ftdi->eeprom = NULL;
     }
-    libusb_exit(ftdi->usb_ctx);
+
+    if (ftdi->usb_ctx)
+    {
+        libusb_exit(ftdi->usb_ctx);
+        ftdi->usb_ctx = NULL;
+    }
 }
 
 /**
@@ -246,10 +273,29 @@ void ftdi_set_usbdev (struct ftdi_context *ftdi, libusb_device_handle *usb)
     ftdi->usb_dev = usb;
 }
 
+/**
+ * @brief Get libftdi library version
+ *
+ * @return ftdi_version_info Library version information
+ **/
+struct ftdi_version_info ftdi_get_library_version(void)
+{
+    struct ftdi_version_info ver;
+
+    ver.major = FTDI_MAJOR_VERSION;
+    ver.minor = FTDI_MINOR_VERSION;
+    ver.micro = FTDI_MICRO_VERSION;
+    ver.version_str = FTDI_VERSION_STRING;
+    ver.snapshot_str = FTDI_SNAPSHOT_VERSION;
+
+    return ver;
+}
 
 /**
-    Finds all ftdi devices on the usb bus. Creates a new ftdi_device_list which
-    needs to be deallocated by ftdi_list_free() after use.
+    Finds all ftdi devices with given VID:PID on the usb bus. Creates a new
+    ftdi_device_list which needs to be deallocated by ftdi_list_free() after
+    use.  With VID:PID 0:0, search for the default devices
+    (0x403:0x6001, 0x403:0x6010, 0x403:0x6011, 0x403:0x6014, 0x403:0x6015)
 
     \param ftdi pointer to ftdi_context
     \param devlist Pointer where to store list of found devices
@@ -258,7 +304,6 @@ void ftdi_set_usbdev (struct ftdi_context *ftdi, libusb_device_handle *usb)
 
     \retval >0: number of devices found
     \retval -3: out of memory
-    \retval -4: libusb_init() failed
     \retval -5: libusb_get_device_list() failed
     \retval -6: libusb_get_device_descriptor() failed
 */
@@ -270,9 +315,6 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli
     int count = 0;
     int i = 0;
 
-    if (libusb_init(&ftdi->usb_ctx) < 0)
-        ftdi_error_return(-4, "libusb_init() failed");
-
     if (libusb_get_device_list(ftdi->usb_ctx, &devs) < 0)
         ftdi_error_return(-5, "libusb_get_device_list() failed");
 
@@ -284,22 +326,27 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli
         struct libusb_device_descriptor desc;
 
         if (libusb_get_device_descriptor(dev, &desc) < 0)
-            ftdi_error_return(-6, "libusb_get_device_descriptor() failed");
-
-        if (desc.idVendor == vendor && desc.idProduct == product)
+            ftdi_error_return_free_device_list(-6, "libusb_get_device_descriptor() failed", devs);
+
+        if (((vendor || product) &&
+                desc.idVendor == vendor && desc.idProduct == product) ||
+                (!(vendor || product) &&
+                 (desc.idVendor == 0x403) && (desc.idProduct == 0x6001 || desc.idProduct == 0x6010
+                                              || desc.idProduct == 0x6011 || desc.idProduct == 0x6014
+                                              || desc.idProduct == 0x6015)))
         {
             *curdev = (struct ftdi_device_list*)malloc(sizeof(struct ftdi_device_list));
             if (!*curdev)
-                ftdi_error_return(-3, "out of memory");
+                ftdi_error_return_free_device_list(-3, "out of memory", devs);
 
             (*curdev)->next = NULL;
             (*curdev)->dev = dev;
-
+            libusb_ref_device(dev);
             curdev = &(*curdev)->next;
             count++;
         }
     }
-
+    libusb_free_device_list(devs,1);
     return count;
 }
 
@@ -315,6 +362,7 @@ void ftdi_list_free(struct ftdi_device_list **devlist)
     for (curdev = *devlist; curdev != NULL;)
     {
         next = curdev->next;
+        libusb_unref_device(curdev->dev);
         free(curdev);
         curdev = next;
     }
@@ -358,15 +406,76 @@ void ftdi_list_free2(struct ftdi_device_list *devlist)
     \retval  -9: get serial number failed
     \retval -11: libusb_get_device_descriptor() failed
 */
-int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct libusb_device * dev,
-                         char * manufacturer, int mnf_len, char * description, int desc_len, char * serial, int serial_len)
+int ftdi_usb_get_strings(struct ftdi_context *ftdi,
+                         struct libusb_device *dev,
+                         char *manufacturer, int mnf_len,
+                         char *description, int desc_len,
+                         char *serial, int serial_len)
+{
+    int ret;
+
+    if ((ftdi==NULL) || (dev==NULL))
+        return -1;
+
+    if (ftdi->usb_dev == NULL && libusb_open(dev, &ftdi->usb_dev) < 0)
+        ftdi_error_return(-4, "libusb_open() failed");
+
+    // ftdi->usb_dev will not be NULL when entering ftdi_usb_get_strings2(), so
+    // it won't be closed either. This allows us to close it whether we actually
+    // called libusb_open() up above or not. This matches the expected behavior
+    // (and note) for ftdi_usb_get_strings().
+    ret = ftdi_usb_get_strings2(ftdi, dev,
+                                manufacturer, mnf_len,
+                                description, desc_len,
+                                serial, serial_len);
+
+    // only close it if it was successful, as all other return codes close
+    // before returning already.
+    if (ret == 0)
+        ftdi_usb_close_internal(ftdi);
+
+    return ret;
+}
+
+/**
+    Return device ID strings from the usb device.
+
+    The parameters manufacturer, description and serial may be NULL
+    or pointer to buffers to store the fetched strings.
+
+    \note The old function ftdi_usb_get_strings() always closes the device.
+          This version only closes the device if it was opened by it.
+
+    \param ftdi pointer to ftdi_context
+    \param dev libusb usb_dev to use
+    \param manufacturer Store manufacturer string here if not NULL
+    \param mnf_len Buffer size of manufacturer string
+    \param description Store product description string here if not NULL
+    \param desc_len Buffer size of product description string
+    \param serial Store serial string here if not NULL
+    \param serial_len Buffer size of serial string
+
+    \retval   0: all fine
+    \retval  -1: wrong arguments
+    \retval  -4: unable to open device
+    \retval  -7: get product manufacturer failed
+    \retval  -8: get product description failed
+    \retval  -9: get serial number failed
+    \retval -11: libusb_get_device_descriptor() failed
+*/
+int ftdi_usb_get_strings2(struct ftdi_context *ftdi, struct libusb_device *dev,
+                          char *manufacturer, int mnf_len,
+                          char *description, int desc_len,
+                          char *serial, int serial_len)
 {
     struct libusb_device_descriptor desc;
+    char need_open;
 
     if ((ftdi==NULL) || (dev==NULL))
         return -1;
 
-    if (libusb_open(dev, &ftdi->usb_dev) < 0)
+    need_open = (ftdi->usb_dev == NULL);
+    if (need_open && libusb_open(dev, &ftdi->usb_dev) < 0)
         ftdi_error_return(-4, "libusb_open() failed");
 
     if (libusb_get_device_descriptor(dev, &desc) < 0)
@@ -399,7 +508,8 @@ int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct libusb_device * dev,
         }
     }
 
-    ftdi_usb_close_internal (ftdi);
+    if (need_open)
+        ftdi_usb_close_internal (ftdi);
 
     return 0;
 }
@@ -423,7 +533,7 @@ static unsigned int _ftdi_determine_max_packet_size(struct ftdi_context *ftdi, l
     // Determine maximum packet size. Init with default value.
     // New hi-speed devices from FTDI use a packet size of 512 bytes
     // but could be connected to a normal speed USB hub -> 64 bytes packet size.
-    if (ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H)
+    if (ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H || ftdi->type == TYPE_232H)
         packet_size = 512;
     else
         packet_size = 64;
@@ -469,7 +579,7 @@ static unsigned int _ftdi_determine_max_packet_size(struct ftdi_context *ftdi, l
     \retval -8: ftdi context invalid
     \retval -9: libusb_get_device_descriptor() failed
     \retval -10: libusb_get_config_descriptor() failed
-    \retval -11: libusb_etach_kernel_driver() failed
+    \retval -11: libusb_detach_kernel_driver() failed
     \retval -12: libusb_get_configuration() failed
 */
 int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
@@ -498,8 +608,11 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
     // if usb_set_configuration() or usb_claim_interface() fails as the
     // detach operation might be denied and everything still works fine.
     // Likely scenario is a static ftdi_sio kernel module.
-    if (libusb_detach_kernel_driver(ftdi->usb_dev, ftdi->interface) !=0)
-        detach_errno = errno;
+    if (ftdi->module_detach_mode == AUTO_DETACH_SIO_MODULE)
+    {
+        if (libusb_detach_kernel_driver(ftdi->usb_dev, ftdi->interface) !=0)
+            detach_errno = errno;
+    }
 
     if (libusb_get_configuration (ftdi->usb_dev, &cfg) < 0)
         ftdi_error_return(-12, "libusb_get_configuration () failed");
@@ -556,6 +669,10 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
         ftdi->type = TYPE_2232H;
     else if (desc.bcdDevice == 0x800)
         ftdi->type = TYPE_4232H;
+    else if (desc.bcdDevice == 0x900)
+        ftdi->type = TYPE_232H;
+    else if (desc.bcdDevice == 0x1000)
+        ftdi->type = TYPE_230X;
 
     // Determine maximum packet size
     ftdi->max_packet_size = _ftdi_determine_max_packet_size(ftdi, dev);
@@ -601,7 +718,6 @@ int ftdi_usb_open(struct ftdi_context *ftdi, int vendor, int product)
     \retval -7: set baudrate failed
     \retval -8: get product description failed
     \retval -9: get serial number failed
-    \retval -11: libusb_init() failed
     \retval -12: libusb_get_device_list() failed
     \retval -13: libusb_get_device_descriptor() failed
 */
@@ -646,9 +762,6 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
     if (ftdi == NULL)
         ftdi_error_return(-11, "ftdi context invalid");
 
-    if (libusb_init(&ftdi->usb_ctx) < 0)
-        ftdi_error_return(-11, "libusb_init() failed");
-
     if (libusb_get_device_list(ftdi->usb_ctx, &devs) < 0)
         ftdi_error_return(-12, "libusb_get_device_list() failed");
 
@@ -724,7 +837,6 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
     \note The description format may be extended in later versions.
 
     \retval  0: all fine
-    \retval -1: libusb_init() failed
     \retval -2: libusb_get_device_list() failed
     \retval -3: usb device not found
     \retval -4: unable to open device
@@ -752,9 +864,6 @@ int ftdi_usb_open_string(struct ftdi_context *ftdi, const char* description)
         unsigned int bus_number, device_address;
         int i = 0;
 
-        if (libusb_init (&ftdi->usb_ctx) < 0)
-            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");
 
@@ -953,34 +1062,30 @@ int ftdi_usb_close(struct ftdi_context *ftdi)
     return rtn;
 }
 
-/**
-    ftdi_convert_baudrate returns nearest supported baud rate to that requested.
+/*  ftdi_to_clkbits_AM For the AM device, convert a requested baudrate
+                    to encoded divisor and the achievable baudrate
     Function is only used internally
     \internal
+
+    See AN120
+   clk/1   -> 0
+   clk/1.5 -> 1
+   clk/2   -> 2
+   From /2, 0.125/ 0.25 and 0.5 steps may be taken
+   The fractional part has frac_code encoding
 */
-static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
-                                 unsigned short *value, unsigned short *index)
+static int ftdi_to_clkbits_AM(int baudrate, unsigned long *encoded_divisor)
+
 {
+    static const char frac_code[8] = {0, 3, 2, 4, 1, 5, 6, 7};
     static const char am_adjust_up[8] = {0, 0, 0, 1, 0, 3, 2, 1};
     static const char am_adjust_dn[8] = {0, 0, 0, 1, 0, 1, 2, 3};
-    static const char frac_code[8] = {0, 3, 2, 4, 1, 5, 6, 7};
     int divisor, best_divisor, best_baud, best_baud_diff;
-    unsigned long encoded_divisor;
     int i;
-
-    if (baudrate <= 0)
-    {
-        // Return error
-        return -1;
-    }
-
     divisor = 24000000 / baudrate;
 
-    if (ftdi->type == TYPE_AM)
-    {
-        // Round down to supported fraction (AM only)
-        divisor -= am_adjust_dn[divisor & 7];
-    }
+    // Round down to supported fraction (AM only)
+    divisor -= am_adjust_dn[divisor & 7];
 
     // Try this divisor and the one above it (because division rounds down)
     best_divisor = 0;
@@ -998,11 +1103,6 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
             // Round up to minimum supported divisor
             try_divisor = 8;
         }
-        else if (ftdi->type != TYPE_AM && try_divisor < 12)
-        {
-            // BM doesn't support divisors 9 through 11 inclusive
-            try_divisor = 12;
-        }
         else if (divisor < 16)
         {
             // AM doesn't support divisors 9 through 15 inclusive
@@ -1010,23 +1110,12 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
         }
         else
         {
-            if (ftdi->type == TYPE_AM)
-            {
-                // Round up to supported fraction (AM only)
-                try_divisor += am_adjust_up[try_divisor & 7];
-                if (try_divisor > 0x1FFF8)
-                {
-                    // Round down to maximum supported divisor value (for AM)
-                    try_divisor = 0x1FFF8;
-                }
-            }
-            else
+            // Round up to supported fraction (AM only)
+            try_divisor += am_adjust_up[try_divisor & 7];
+            if (try_divisor > 0x1FFF8)
             {
-                if (try_divisor > 0x1FFFF)
-                {
-                    // Round down to maximum supported divisor value (for BM)
-                    try_divisor = 0x1FFFF;
-                }
+                // Round down to maximum supported divisor value (for AM)
+                try_divisor = 0x1FFF8;
             }
         }
         // Get estimated baud rate (to nearest integer)
@@ -1054,19 +1143,127 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
         }
     }
     // Encode the best divisor value
-    encoded_divisor = (best_divisor >> 3) | (frac_code[best_divisor & 7] << 14);
+    *encoded_divisor = (best_divisor >> 3) | (frac_code[best_divisor & 7] << 14);
     // Deal with special cases for encoded value
-    if (encoded_divisor == 1)
+    if (*encoded_divisor == 1)
+    {
+        *encoded_divisor = 0;    // 3000000 baud
+    }
+    else if (*encoded_divisor == 0x4001)
+    {
+        *encoded_divisor = 1;    // 2000000 baud (BM only)
+    }
+    return best_baud;
+}
+
+/*  ftdi_to_clkbits Convert a requested baudrate for a given system clock  and predivisor
+                    to encoded divisor and the achievable baudrate
+    Function is only used internally
+    \internal
+
+    See AN120
+   clk/1   -> 0
+   clk/1.5 -> 1
+   clk/2   -> 2
+   From /2, 0.125 steps may be taken.
+   The fractional part has frac_code encoding
+
+   value[13:0] of value is the divisor
+   index[9] mean 12 MHz Base(120 MHz/10) rate versus 3 MHz (48 MHz/16) else
+
+   H Type have all features above with
+   {index[8],value[15:14]} is the encoded subdivisor
+
+   FT232R, FT2232 and FT232BM have no option for 12 MHz and with
+   {index[0],value[15:14]} is the encoded subdivisor
+
+   AM Type chips have only four fractional subdivisors at value[15:14]
+   for subdivisors 0, 0.5, 0.25, 0.125
+*/
+static int ftdi_to_clkbits(int baudrate, unsigned int clk, int clk_div, unsigned long *encoded_divisor)
+{
+    static const char frac_code[8] = {0, 3, 2, 4, 1, 5, 6, 7};
+    int best_baud = 0;
+    int divisor, best_divisor;
+    if (baudrate >=  clk/clk_div)
+    {
+        *encoded_divisor = 0;
+        best_baud = clk/clk_div;
+    }
+    else if (baudrate >=  clk/(clk_div + clk_div/2))
     {
-        encoded_divisor = 0;    // 3000000 baud
+        *encoded_divisor = 1;
+        best_baud = clk/(clk_div + clk_div/2);
     }
-    else if (encoded_divisor == 0x4001)
+    else if (baudrate >=  clk/(2*clk_div))
     {
-        encoded_divisor = 1;    // 2000000 baud (BM only)
+        *encoded_divisor = 2;
+        best_baud = clk/(2*clk_div);
+    }
+    else
+    {
+        /* We divide by 16 to have 3 fractional bits and one bit for rounding */
+        divisor = clk*16/clk_div / baudrate;
+        if (divisor & 1) /* Decide if to round up or down*/
+            best_divisor = divisor /2 +1;
+        else
+            best_divisor = divisor/2;
+        if(best_divisor > 0x20000)
+            best_divisor = 0x1ffff;
+        best_baud = clk*16/clk_div/best_divisor;
+        if (best_baud & 1) /* Decide if to round up or down*/
+            best_baud = best_baud /2 +1;
+        else
+            best_baud = best_baud /2;
+        *encoded_divisor = (best_divisor >> 3) | (frac_code[best_divisor & 0x7] << 14);
+    }
+    return best_baud;
+}
+/**
+    ftdi_convert_baudrate returns nearest supported baud rate to that requested.
+    Function is only used internally
+    \internal
+*/
+static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
+                                 unsigned short *value, unsigned short *index)
+{
+    int best_baud;
+    unsigned long encoded_divisor;
+
+    if (baudrate <= 0)
+    {
+        // Return error
+        return -1;
+    }
+
+#define H_CLK 120000000
+#define C_CLK  48000000
+    if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H) || (ftdi->type == TYPE_232H))
+    {
+        if(baudrate*10 > H_CLK /0x3fff)
+        {
+            /* On H Devices, use 12 000 000 Baudrate when possible
+               We have a 14 bit divisor, a 1 bit divisor switch (10 or 16)
+               three fractional bits and a 120 MHz clock
+               Assume AN_120 "Sub-integer divisors between 0 and 2 are not allowed" holds for
+               DIV/10 CLK too, so /1, /1.5 and /2 can be handled the same*/
+            best_baud = ftdi_to_clkbits(baudrate, H_CLK, 10, &encoded_divisor);
+            encoded_divisor |= 0x20000; /* switch on CLK/10*/
+        }
+        else
+            best_baud = ftdi_to_clkbits(baudrate, C_CLK, 16, &encoded_divisor);
+    }
+    else if ((ftdi->type == TYPE_BM) || (ftdi->type == TYPE_2232C) || (ftdi->type == TYPE_R) || (ftdi->type == TYPE_230X))
+    {
+        best_baud = ftdi_to_clkbits(baudrate, C_CLK, 16, &encoded_divisor);
+    }
+    else
+    {
+        best_baud = ftdi_to_clkbits_AM(baudrate, &encoded_divisor);
     }
     // Split into "value" and "index" values
     *value = (unsigned short)(encoded_divisor & 0xFFFF);
-    if (ftdi->type == TYPE_2232C || ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H)
+    if (ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H || ftdi->type == TYPE_232H)
     {
         *index = (unsigned short)(encoded_divisor >> 8);
         *index &= 0xFF00;
@@ -1080,6 +1277,16 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
 }
 
 /**
+ * @brief Wrapper function to export ftdi_convert_baudrate() to the unit test
+ * Do not use, it's only for the unit test framework
+ **/
+int convert_baudrate_UT_export(int baudrate, struct ftdi_context *ftdi,
+                               unsigned short *value, unsigned short *index)
+{
+    return ftdi_convert_baudrate(baudrate, ftdi, value, index);
+}
+
+/**
     Sets the chip baud rate
 
     \param ftdi pointer to ftdi_context
@@ -1225,7 +1432,7 @@ int ftdi_set_line_property2(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
     \retval <0: error code from usb_bulk_write()
     \retval >0: number of bytes written
 */
-int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
+int ftdi_write_data(struct ftdi_context *ftdi, const unsigned char *buf, int size)
 {
     int offset = 0;
     int actual_length;
@@ -1240,7 +1447,7 @@ int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
         if (offset+write_size > size)
             write_size = size-offset;
 
-        if (libusb_bulk_transfer(ftdi->usb_dev, ftdi->in_ep, buf+offset, write_size, &actual_length, ftdi->usb_write_timeout) < 0)
+        if (libusb_bulk_transfer(ftdi->usb_dev, ftdi->in_ep, (unsigned char *)buf+offset, write_size, &actual_length, ftdi->usb_write_timeout) < 0)
             ftdi_error_return(-1, "usb bulk write failed");
 
         offset += actual_length;
@@ -1249,7 +1456,7 @@ int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
     return offset;
 }
 
-static void ftdi_read_data_cb(struct libusb_transfer *transfer)
+static void LIBUSB_CALL ftdi_read_data_cb(struct libusb_transfer *transfer)
 {
     struct ftdi_transfer_control *tc = (struct ftdi_transfer_control *) transfer->user_data;
     struct ftdi_context *ftdi = tc->ftdi;
@@ -1325,13 +1532,19 @@ static void ftdi_read_data_cb(struct libusb_transfer *transfer)
             }
         }
     }
-    ret = libusb_submit_transfer (transfer);
-    if (ret < 0)
-        tc->completed = 1;
+
+    if (transfer->status == LIBUSB_TRANSFER_CANCELLED)
+        tc->completed = LIBUSB_TRANSFER_CANCELLED;
+    else
+    {
+        ret = libusb_submit_transfer (transfer);
+        if (ret < 0)
+            tc->completed = 1;
+    }
 }
 
 
-static void ftdi_write_data_cb(struct libusb_transfer *transfer)
+static void LIBUSB_CALL 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;
@@ -1352,9 +1565,15 @@ static void ftdi_write_data_cb(struct libusb_transfer *transfer)
 
         transfer->length = write_size;
         transfer->buffer = tc->buf + tc->offset;
-        ret = libusb_submit_transfer (transfer);
-        if (ret < 0)
-            tc->completed = 1;
+
+        if (transfer->status == LIBUSB_TRANSFER_CANCELLED)
+            tc->completed = LIBUSB_TRANSFER_CANCELLED;
+        else
+        {
+            ret = libusb_submit_transfer (transfer);
+            if (ret < 0)
+                tc->completed = 1;
+        }
     }
 }
 
@@ -1399,7 +1618,7 @@ struct ftdi_transfer_control *ftdi_write_data_submit(struct ftdi_context *ftdi,
     tc->size = size;
     tc->offset = 0;
 
-    if (size < ftdi->writebuffer_chunksize)
+    if (size < (int)ftdi->writebuffer_chunksize)
         write_size = size;
     else
         write_size = ftdi->writebuffer_chunksize;
@@ -1452,7 +1671,7 @@ struct ftdi_transfer_control *ftdi_read_data_submit(struct ftdi_context *ftdi, u
     tc->buf = buf;
     tc->size = size;
 
-    if (size <= ftdi->readbuffer_remaining)
+    if (size <= (int)ftdi->readbuffer_remaining)
     {
         memcpy (buf, ftdi->readbuffer+ftdi->readbuffer_offset, size);
 
@@ -1517,17 +1736,19 @@ struct ftdi_transfer_control *ftdi_read_data_submit(struct ftdi_context *ftdi, u
 int ftdi_transfer_data_done(struct ftdi_transfer_control *tc)
 {
     int ret;
-
+    struct timeval to = { 0, 0 };
     while (!tc->completed)
     {
-        ret = libusb_handle_events(tc->ftdi->usb_ctx);
+        ret = libusb_handle_events_timeout_completed(tc->ftdi->usb_ctx,
+                &to, &tc->completed);
         if (ret < 0)
         {
             if (ret == LIBUSB_ERROR_INTERRUPTED)
                 continue;
             libusb_cancel_transfer(tc->transfer);
             while (!tc->completed)
-                if (libusb_handle_events(tc->ftdi->usb_ctx) < 0)
+                if (libusb_handle_events_timeout_completed(tc->ftdi->usb_ctx,
+                        &to, &tc->completed) < 0)
                     break;
             libusb_free_transfer(tc->transfer);
             free (tc);
@@ -1551,6 +1772,39 @@ int ftdi_transfer_data_done(struct ftdi_transfer_control *tc)
 }
 
 /**
+    Cancel transfer and wait for completion.
+
+    Use libusb 1.0 asynchronous API.
+
+    \param tc pointer to ftdi_transfer_control
+    \param to pointer to timeout value or NULL for infinite
+*/
+
+void ftdi_transfer_data_cancel(struct ftdi_transfer_control *tc,
+                               struct timeval * to)
+{
+    struct timeval tv = { 0, 0 };
+
+    if (!tc->completed && tc->transfer != NULL)
+    {
+        if (to == NULL)
+            to = &tv;
+
+        libusb_cancel_transfer(tc->transfer);
+        while (!tc->completed)
+        {
+            if (libusb_handle_events_timeout_completed(tc->ftdi->usb_ctx, to, &tc->completed) < 0)
+                break;
+        }
+    }
+
+    if (tc->transfer)
+        libusb_free_transfer(tc->transfer);
+
+    free (tc);
+}
+
+/**
     Configure write buffer chunk size.
     Default is 4096.
 
@@ -1616,7 +1870,7 @@ int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
         ftdi_error_return(-1, "max_packet_size is bogus (zero)");
 
     // everything we want is still in the readbuffer?
-    if (size <= ftdi->readbuffer_remaining)
+    if (size <= (int)ftdi->readbuffer_remaining)
     {
         memcpy (buf, ftdi->readbuffer+ftdi->readbuffer_offset, size);
 
@@ -1773,21 +2027,19 @@ int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunks
     return 0;
 }
 
-
 /**
-    Enable bitbang mode.
-
-    \deprecated use \ref ftdi_set_bitmode with mode BITMODE_BITBANG instead
+    Enable/disable bitbang modes.
 
     \param ftdi pointer to ftdi_context
     \param bitmask Bitmask to configure lines.
            HIGH/ON value configures a line as output.
+    \param mode Bitbang mode: use the values defined in \ref ftdi_mpsse_mode
 
     \retval  0: all fine
     \retval -1: can't enable bitbang mode
     \retval -2: USB device unavailable
 */
-int ftdi_enable_bitbang(struct ftdi_context *ftdi, unsigned char bitmask)
+int ftdi_set_bitmode(struct ftdi_context *ftdi, unsigned char bitmask, unsigned char mode)
 {
     unsigned short usb_val;
 
@@ -1795,15 +2047,12 @@ int ftdi_enable_bitbang(struct ftdi_context *ftdi, unsigned char bitmask)
         ftdi_error_return(-2, "USB device unavailable");
 
     usb_val = bitmask; // low byte: bitmask
-    /* FT2232C: Set bitbang_mode to 2 to enable SPI */
-    usb_val |= (ftdi->bitbang_mode << 8);
-
-    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
-                                SIO_SET_BITMODE_REQUEST, usb_val, ftdi->index,
-                                NULL, 0, ftdi->usb_write_timeout) < 0)
-        ftdi_error_return(-1, "unable to enter bitbang mode. Perhaps not a BM type chip?");
+    usb_val |= (mode << 8);
+    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_BITMODE_REQUEST, usb_val, ftdi->index, NULL, 0, ftdi->usb_write_timeout) < 0)
+        ftdi_error_return(-1, "unable to configure bitbang mode. Perhaps not a BM/2232C type chip?");
 
-    ftdi->bitbang_enabled = 1;
+    ftdi->bitbang_mode = mode;
+    ftdi->bitbang_enabled = (mode == BITMODE_RESET) ? 0 : 1;
     return 0;
 }
 
@@ -1828,34 +2077,6 @@ int ftdi_disable_bitbang(struct ftdi_context *ftdi)
     return 0;
 }
 
-/**
-    Enable/disable bitbang modes.
-
-    \param ftdi pointer to ftdi_context
-    \param bitmask Bitmask to configure lines.
-           HIGH/ON value configures a line as output.
-    \param mode Bitbang mode: use the values defined in \ref ftdi_mpsse_mode
-
-    \retval  0: all fine
-    \retval -1: can't enable bitbang mode
-    \retval -2: USB device unavailable
-*/
-int ftdi_set_bitmode(struct ftdi_context *ftdi, unsigned char bitmask, unsigned char mode)
-{
-    unsigned short usb_val;
-
-    if (ftdi == NULL || ftdi->usb_dev == NULL)
-        ftdi_error_return(-2, "USB device unavailable");
-
-    usb_val = bitmask; // low byte: bitmask
-    usb_val |= (mode << 8);
-    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_BITMODE_REQUEST, usb_val, ftdi->index, NULL, 0, ftdi->usb_write_timeout) < 0)
-        ftdi_error_return(-1, "unable to configure bitbang mode. Perhaps not a 2232C type chip?");
-
-    ftdi->bitbang_mode = mode;
-    ftdi->bitbang_enabled = (mode == BITMODE_RESET) ? 0 : 1;
-    return 0;
-}
 
 /**
     Directly read pin state, circumventing the read buffer. Useful for bitbang mode.
@@ -1984,7 +2205,7 @@ int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status)
     if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_POLL_MODEM_STATUS_REQUEST, 0, ftdi->index, (unsigned char *)usb_val, 2, ftdi->usb_read_timeout) != 2)
         ftdi_error_return(-1, "getting modem status failed");
 
-    *status = (usb_val[1] << 8) | usb_val[0];
+    *status = (usb_val[1] << 8) | (usb_val[0] & 0xFF);
 
     return 0;
 }
@@ -2168,7 +2389,7 @@ int ftdi_set_error_char(struct ftdi_context *ftdi,
 }
 
 /**
-    Init eeprom with default values.
+    Init eeprom with default values for the connected device
     \param ftdi pointer to ftdi_context
     \param manufacturer String to use as Manufacturer
     \param product String to use as Product description
@@ -2177,6 +2398,7 @@ int ftdi_set_error_char(struct ftdi_context *ftdi,
     \retval  0: all fine
     \retval -1: No struct ftdi_context
     \retval -2: No struct ftdi_eeprom
+    \retval -3: No connected device or device not yet opened
 */
 int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
                              char * product, char * serial)
@@ -2192,13 +2414,23 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
     eeprom = ftdi->eeprom;
     memset(eeprom, 0, sizeof(struct ftdi_eeprom));
 
+    if (ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "No connected device or device not yet opened");
+
     eeprom->vendor_id = 0x0403;
-    eeprom->use_serial = USE_SERIAL_NUM;
+    eeprom->use_serial = 1;
     if ((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM) ||
             (ftdi->type == TYPE_R))
         eeprom->product_id = 0x6001;
+    else if (ftdi->type == TYPE_4232H)
+        eeprom->product_id = 0x6011;
+    else if (ftdi->type == TYPE_232H)
+        eeprom->product_id = 0x6014;
+    else if (ftdi->type == TYPE_230X)
+        eeprom->product_id = 0x6015;
     else
         eeprom->product_id = 0x6010;
+
     if (ftdi->type == TYPE_AM)
         eeprom->usb_version = 0x0101;
     else
@@ -2210,7 +2442,7 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
     eeprom->manufacturer = NULL;
     if (manufacturer)
     {
-        eeprom->manufacturer = malloc(strlen(manufacturer)+1);
+        eeprom->manufacturer = (char *)malloc(strlen(manufacturer)+1);
         if (eeprom->manufacturer)
             strcpy(eeprom->manufacturer, manufacturer);
     }
@@ -2218,23 +2450,43 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
     if (eeprom->product)
         free (eeprom->product);
     eeprom->product = NULL;
+    if(product)
     {
-        eeprom->product = malloc(strlen(product)+1);
+        eeprom->product = (char *)malloc(strlen(product)+1);
         if (eeprom->product)
             strcpy(eeprom->product, product);
     }
+    else
+    {
+        const char* default_product;
+        switch(ftdi->type)
+        {
+            case TYPE_AM:    default_product = "AM"; break;
+            case TYPE_BM:    default_product = "BM"; break;
+            case TYPE_2232C: default_product = "Dual RS232"; break;
+            case TYPE_R:     default_product = "FT232R USB UART"; break;
+            case TYPE_2232H: default_product = "Dual RS232-HS"; break;
+            case TYPE_4232H: default_product = "FT4232H"; break;
+            case TYPE_232H:  default_product = "Single-RS232-HS"; break;
+            case TYPE_230X:  default_product = "FT230X Basic UART"; break;
+            default:
+                ftdi_error_return(-3, "Unknown chip type");
+        }
+        eeprom->product = (char *)malloc(strlen(default_product) +1);
+        if (eeprom->product)
+            strcpy(eeprom->product, default_product);
+    }
 
     if (eeprom->serial)
         free (eeprom->serial);
     eeprom->serial = NULL;
     if (serial)
     {
-        eeprom->serial = malloc(strlen(serial)+1);
+        eeprom->serial = (char *)malloc(strlen(serial)+1);
         if (eeprom->serial)
             strcpy(eeprom->serial, serial);
     }
 
-
     if (ftdi->type == TYPE_R)
     {
         eeprom->max_power = 90;
@@ -2245,8 +2497,168 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
         eeprom->cbus_function[3] = CBUS_PWREN;
         eeprom->cbus_function[4] = CBUS_SLEEP;
     }
+    else if (ftdi->type == TYPE_230X)
+    {
+        eeprom->max_power = 90;
+        eeprom->size = 0x100;
+        eeprom->cbus_function[0] = CBUSX_TXDEN;
+        eeprom->cbus_function[1] = CBUSX_RXLED;
+        eeprom->cbus_function[2] = CBUSX_TXLED;
+        eeprom->cbus_function[3] = CBUSX_SLEEP;
+    }
     else
+    {
+        if(ftdi->type == TYPE_232H)
+        {
+            int i;
+            for (i=0; i<10; i++)
+                eeprom->cbus_function[i] = CBUSH_TRISTATE;
+        }
         eeprom->size = -1;
+    }
+    switch (ftdi->type)
+    {
+        case TYPE_AM:
+            eeprom->release_number = 0x0200;
+            break;
+        case TYPE_BM:
+            eeprom->release_number = 0x0400;
+            break;
+        case TYPE_2232C:
+            eeprom->release_number = 0x0500;
+            break;
+        case TYPE_R:
+            eeprom->release_number = 0x0600;
+            break;
+        case TYPE_2232H:
+            eeprom->release_number = 0x0700;
+            break;
+        case TYPE_4232H:
+            eeprom->release_number = 0x0800;
+            break;
+        case TYPE_232H:
+            eeprom->release_number = 0x0900;
+            break;
+        case TYPE_230X:
+            eeprom->release_number = 0x1000;
+            break;
+        default:
+            eeprom->release_number = 0x00;
+    }
+    return 0;
+}
+
+int ftdi_eeprom_set_strings(struct ftdi_context *ftdi, char * manufacturer,
+                            char * product, char * serial)
+{
+    struct ftdi_eeprom *eeprom;
+
+    if (ftdi == NULL)
+        ftdi_error_return(-1, "No struct ftdi_context");
+
+    if (ftdi->eeprom == NULL)
+        ftdi_error_return(-2,"No struct ftdi_eeprom");
+
+    eeprom = ftdi->eeprom;
+
+    if (ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "No connected device or device not yet opened");
+
+    if (manufacturer)
+    {
+        if (eeprom->manufacturer)
+            free (eeprom->manufacturer);
+        eeprom->manufacturer = (char *)malloc(strlen(manufacturer)+1);
+        if (eeprom->manufacturer)
+            strcpy(eeprom->manufacturer, manufacturer);
+    }
+
+    if(product)
+    {
+        if (eeprom->product)
+            free (eeprom->product);
+        eeprom->product = (char *)malloc(strlen(product)+1);
+        if (eeprom->product)
+            strcpy(eeprom->product, product);
+    }
+
+    if (serial)
+    {
+        if (eeprom->serial)
+            free (eeprom->serial);
+        eeprom->serial = (char *)malloc(strlen(serial)+1);
+        if (eeprom->serial)
+        {
+            strcpy(eeprom->serial, serial);
+            eeprom->use_serial = 1;
+        }
+    }
+    return 0;
+}
+
+
+/*FTD2XX doesn't check for values not fitting in the ACBUS Signal options*/
+void set_ft232h_cbus(struct ftdi_eeprom *eeprom, unsigned char * output)
+{
+    int i;
+    for(i=0; i<5; i++)
+    {
+        int mode_low, mode_high;
+        if (eeprom->cbus_function[2*i]> CBUSH_CLK7_5)
+            mode_low = CBUSH_TRISTATE;
+        else
+            mode_low = eeprom->cbus_function[2*i];
+        if (eeprom->cbus_function[2*i+1]> CBUSH_CLK7_5)
+            mode_high = CBUSH_TRISTATE;
+        else
+            mode_high = eeprom->cbus_function[2*i+1];
+
+        output[0x18+i] = (mode_high <<4) | mode_low;
+    }
+}
+/* Return the bits for the encoded EEPROM Structure of a requested Mode
+ *
+ */
+static unsigned char type2bit(unsigned char type, enum ftdi_chip_type chip)
+{
+    switch (chip)
+    {
+        case TYPE_2232H:
+        case TYPE_2232C:
+        {
+            switch (type)
+            {
+                case CHANNEL_IS_UART: return 0;
+                case CHANNEL_IS_FIFO: return 0x01;
+                case CHANNEL_IS_OPTO: return 0x02;
+                case CHANNEL_IS_CPU : return 0x04;
+                default: return 0;
+            }
+        }
+        case TYPE_232H:
+        {
+            switch (type)
+            {
+                case CHANNEL_IS_UART   : return 0;
+                case CHANNEL_IS_FIFO   : return 0x01;
+                case CHANNEL_IS_OPTO   : return 0x02;
+                case CHANNEL_IS_CPU    : return 0x04;
+                case CHANNEL_IS_FT1284 : return 0x08;
+                default: return 0;
+            }
+        }
+        case TYPE_R:
+        {
+            switch (type)
+            {
+                case CHANNEL_IS_UART   : return 0;
+                case CHANNEL_IS_FIFO   : return 0x01;
+                default: return 0;
+            }
+        }
+        case TYPE_230X: /* FT230X is only UART */
+        default: return 0;
+    }
     return 0;
 }
 
@@ -2269,7 +2681,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     unsigned char i, j, eeprom_size_mask;
     unsigned short checksum, value;
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
-    int user_area_size;
+    int user_area_size, free_start, free_end;
     struct ftdi_eeprom *eeprom;
     unsigned char * output;
 
@@ -2284,10 +2696,13 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     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->size == -1)
+    {
+        if ((eeprom->chip == 0x56) || (eeprom->chip == 0x66))
+            eeprom->size = 0x100;
+        else
+            eeprom->size = 0x80;
+    }
 
     if (eeprom->manufacturer != NULL)
         manufacturer_size = strlen(eeprom->manufacturer);
@@ -2301,18 +2716,22 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     {
         case TYPE_AM:
         case TYPE_BM:
+        case TYPE_R:
             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:
+        case TYPE_230X:
             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;
+        case TYPE_232H:
+            user_area_size = 80;
+            break;
         default:
             user_area_size = 0;
             break;
@@ -2323,7 +2742,17 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
         ftdi_error_return(-1,"eeprom size exceeded");
 
     // empty eeprom
-    memset (ftdi->eeprom->buf, 0, FTDI_MAX_EEPROM_SIZE);
+    if (ftdi->type == TYPE_230X)
+    {
+        /* FT230X have a reserved section in the middle of the MTP,
+           which cannot be written to, but must be included in the checksum */
+        memset(ftdi->eeprom->buf, 0, 0x80);
+        memset((ftdi->eeprom->buf + 0xa0), 0, (FTDI_MAX_EEPROM_SIZE - 0xa0));
+    }
+    else
+    {
+        memset(ftdi->eeprom->buf, 0, FTDI_MAX_EEPROM_SIZE);
+    }
 
     // Bytes and Bits set for all Types
 
@@ -2336,30 +2765,8 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     output[0x05] = eeprom->product_id >> 8;
 
     // Addr 06: Device release number (0400h for BM features)
-    output[0x06] = 0x00;
-    switch (ftdi->type)
-    {
-        case TYPE_AM:
-            output[0x07] = 0x02;
-            break;
-        case TYPE_BM:
-            output[0x07] = 0x04;
-            break;
-        case TYPE_2232C:
-            output[0x07] = 0x05;
-            break;
-        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;
-    }
+    output[0x06] = eeprom->release_number;
+    output[0x07] = eeprom->release_number >> 8;
 
     // Addr 08: Config descriptor
     // Bit 7: always 1
@@ -2367,16 +2774,16 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     // Bit 5: 1 if this device uses remote wakeup
     // Bit 4-0: reserved - 0
     j = 0x80;
-    if (eeprom->self_powered == 1)
+    if (eeprom->self_powered)
         j |= 0x40;
-    if (eeprom->remote_wakeup == 1)
+    if (eeprom->remote_wakeup)
         j |= 0x20;
     output[0x08] = j;
 
     // Addr 09: Max power consumption: max power = value * 2 mA
-    output[0x09] = eeprom->max_power>>1;
+    output[0x09] = eeprom->max_power / MAX_POWER_MILLIAMP_PER_UNIT;
 
-    if (ftdi->type != TYPE_AM)
+    if ((ftdi->type != TYPE_AM) && (ftdi->type != TYPE_230X))
     {
         // Addr 0A: Chip configuration
         // Bit 7: 0 - reserved
@@ -2389,9 +2796,9 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
         // Bit 0: 1 - In EndPoint is Isochronous
         //
         j = 0;
-        if (eeprom->in_is_isochronous == 1)
+        if (eeprom->in_is_isochronous)
             j = j | 1;
-        if (eeprom->out_is_isochronous == 1)
+        if (eeprom->out_is_isochronous)
             j = j | 2;
         output[0x0A] = j;
     }
@@ -2399,6 +2806,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     // Dynamic content
     // Strings start at 0x94 (TYPE_AM, TYPE_BM)
     // 0x96 (TYPE_2232C), 0x98 (TYPE_R) and 0x9a (TYPE_x232H)
+    // 0xa0 (TYPE_232H)
     i = 0;
     switch (ftdi->type)
     {
@@ -2412,9 +2820,15 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
         case TYPE_AM:
         case TYPE_BM:
             i += 0x94;
+            break;
+        case TYPE_232H:
+        case TYPE_230X:
+            i = 0xa0;
+            break;
     }
     /* Wrap around 0x80 for 128 byte EEPROMS (Internale and 93x46) */
     eeprom_size_mask = eeprom->size -1;
+    free_end = i & eeprom_size_mask;
 
     // Addr 0E: Offset of the manufacturer string + 0x80, calculated later
     // Addr 0F: Length of manufacturer string
@@ -2467,7 +2881,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
     if (ftdi->type > TYPE_AM) /* use_serial not used in AM devices */
     {
-        if (eeprom->use_serial == USE_SERIAL_NUM )
+        if (eeprom->use_serial)
             output[0x0A] |= USE_SERIAL_NUM;
         else
             output[0x0A] &= ~USE_SERIAL_NUM;
@@ -2482,7 +2896,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
         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)
+            if (eeprom->use_usb_version)
                 output[0x0A] |= USE_USB_VERSION_BIT;
             else
                 output[0x0A] &= ~USE_USB_VERSION_BIT;
@@ -2490,7 +2904,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             break;
         case TYPE_2232C:
 
-            output[0x00] = (eeprom->channel_a_type);
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_2232C);
             if ( eeprom->channel_a_driver == DRIVER_VCP)
                 output[0x00] |= DRIVER_VCP;
             else
@@ -2501,7 +2915,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             else
                 output[0x00] &= ~HIGH_CURRENT_DRIVE;
 
-            output[0x01] = (eeprom->channel_b_type);
+            output[0x01] = type2bit(eeprom->channel_b_type, TYPE_2232C);
             if ( eeprom->channel_b_driver == DRIVER_VCP)
                 output[0x01] |= DRIVER_VCP;
             else
@@ -2512,19 +2926,19 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             else
                 output[0x01] &= ~HIGH_CURRENT_DRIVE;
 
-            if (eeprom->in_is_isochronous == 1)
+            if (eeprom->in_is_isochronous)
                 output[0x0A] |= 0x1;
             else
                 output[0x0A] &= ~0x1;
-            if (eeprom->out_is_isochronous == 1)
+            if (eeprom->out_is_isochronous)
                 output[0x0A] |= 0x2;
             else
                 output[0x0A] &= ~0x2;
-            if (eeprom->suspend_pull_downs == 1)
+            if (eeprom->suspend_pull_downs)
                 output[0x0A] |= 0x4;
             else
                 output[0x0A] &= ~0x4;
-            if (eeprom->use_usb_version == USE_USB_VERSION_BIT)
+            if (eeprom->use_usb_version)
                 output[0x0A] |= USE_USB_VERSION_BIT;
             else
                 output[0x0A] &= ~USE_USB_VERSION_BIT;
@@ -2534,11 +2948,14 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             output[0x14] = eeprom->chip;
             break;
         case TYPE_R:
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_R);
             if (eeprom->high_current == HIGH_CURRENT_DRIVE_R)
                 output[0x00] |= HIGH_CURRENT_DRIVE_R;
+            if (eeprom->external_oscillator)
+                output[0x00] |= 0x02;
             output[0x01] = 0x40; /* Hard coded Endpoint Size*/
 
-            if (eeprom->suspend_pull_downs == 1)
+            if (eeprom->suspend_pull_downs)
                 output[0x0A] |= 0x4;
             else
                 output[0x0A] &= ~0x4;
@@ -2546,22 +2963,22 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             output[0x0C] = eeprom->usb_version & 0xff;
             output[0x0D] = (eeprom->usb_version>>8) & 0xff;
 
-            if (eeprom->cbus_function[0] > CBUS_BB)
+            if (eeprom->cbus_function[0] > CBUS_BB_RD)
                 output[0x14] = CBUS_TXLED;
             else
                 output[0x14] = eeprom->cbus_function[0];
 
-            if (eeprom->cbus_function[1] > CBUS_BB)
+            if (eeprom->cbus_function[1] > CBUS_BB_RD)
                 output[0x14] |= CBUS_RXLED<<4;
             else
                 output[0x14] |= eeprom->cbus_function[1]<<4;
 
-            if (eeprom->cbus_function[2] > CBUS_BB)
+            if (eeprom->cbus_function[2] > CBUS_BB_RD)
                 output[0x15] = CBUS_TXDEN;
             else
                 output[0x15] = eeprom->cbus_function[2];
 
-            if (eeprom->cbus_function[3] > CBUS_BB)
+            if (eeprom->cbus_function[3] > CBUS_BB_RD)
                 output[0x15] |= CBUS_PWREN<<4;
             else
                 output[0x15] |= eeprom->cbus_function[3]<<4;
@@ -2572,13 +2989,13 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
                 output[0x16] = eeprom->cbus_function[4];
             break;
         case TYPE_2232H:
-            output[0x00] = (eeprom->channel_a_type);
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_2232H);
             if ( eeprom->channel_a_driver == DRIVER_VCP)
                 output[0x00] |= DRIVER_VCP;
             else
                 output[0x00] &= ~DRIVER_VCP;
 
-            output[0x01] = (eeprom->channel_b_type);
+            output[0x01] = type2bit(eeprom->channel_b_type, TYPE_2232H);
             if ( eeprom->channel_b_driver == DRIVER_VCP)
                 output[0x01] |= DRIVER_VCP;
             else
@@ -2588,7 +3005,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             else
                 output[0x01] &= ~SUSPEND_DBUS7_BIT;
 
-            if (eeprom->suspend_pull_downs == 1)
+            if (eeprom->suspend_pull_downs)
                 output[0x0A] |= 0x4;
             else
                 output[0x0A] &= ~0x4;
@@ -2633,7 +3050,178 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
             break;
         case TYPE_4232H:
-            fprintf(stderr,"FIXME: Build FT4232H specific EEPROM settings\n");
+            if (eeprom->channel_a_driver == DRIVER_VCP)
+                output[0x00] |= DRIVER_VCP;
+            else
+                output[0x00] &= ~DRIVER_VCP;
+            if (eeprom->channel_b_driver == DRIVER_VCP)
+                output[0x01] |= DRIVER_VCP;
+            else
+                output[0x01] &= ~DRIVER_VCP;
+            if (eeprom->channel_c_driver == DRIVER_VCP)
+                output[0x00] |= (DRIVER_VCP << 4);
+            else
+                output[0x00] &= ~(DRIVER_VCP << 4);
+            if (eeprom->channel_d_driver == DRIVER_VCP)
+                output[0x01] |= (DRIVER_VCP << 4);
+            else
+                output[0x01] &= ~(DRIVER_VCP << 4);
+
+            if (eeprom->suspend_pull_downs)
+                output[0x0a] |= 0x4;
+            else
+                output[0x0a] &= ~0x4;
+
+            if (eeprom->channel_a_rs485enable)
+                output[0x0b] |= CHANNEL_IS_RS485 << 0;
+            else
+                output[0x0b] &= ~(CHANNEL_IS_RS485 << 0);
+            if (eeprom->channel_b_rs485enable)
+                output[0x0b] |= CHANNEL_IS_RS485 << 1;
+            else
+                output[0x0b] &= ~(CHANNEL_IS_RS485 << 1);
+            if (eeprom->channel_c_rs485enable)
+                output[0x0b] |= CHANNEL_IS_RS485 << 2;
+            else
+                output[0x0b] &= ~(CHANNEL_IS_RS485 << 2);
+            if (eeprom->channel_d_rs485enable)
+                output[0x0b] |= CHANNEL_IS_RS485 << 3;
+            else
+                output[0x0b] &= ~(CHANNEL_IS_RS485 << 3);
+
+            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_232H:
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_232H);
+            if ( eeprom->channel_a_driver == DRIVER_VCP)
+                output[0x00] |= DRIVER_VCPH;
+            else
+                output[0x00] &= ~DRIVER_VCPH;
+            if (eeprom->powersave)
+                output[0x01] |= POWER_SAVE_DISABLE_H;
+            else
+                output[0x01] &= ~POWER_SAVE_DISABLE_H;
+
+            if (eeprom->suspend_pull_downs)
+                output[0x0a] |= 0x4;
+            else
+                output[0x0a] &= ~0x4;
+
+            if (eeprom->clock_polarity)
+                output[0x01] |= FT1284_CLK_IDLE_STATE;
+            else
+                output[0x01] &= ~FT1284_CLK_IDLE_STATE;
+            if (eeprom->data_order)
+                output[0x01] |= FT1284_DATA_LSB;
+            else
+                output[0x01] &= ~FT1284_DATA_LSB;
+            if (eeprom->flow_control)
+                output[0x01] |= FT1284_FLOW_CONTROL;
+            else
+                output[0x01] &= ~FT1284_FLOW_CONTROL;
+            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[0x0d] |= DRIVE_16MA;
+            else
+                output[0x0d] |= eeprom->group1_drive;
+            if (eeprom->group1_schmitt == IS_SCHMITT)
+                output[0x0d] |= IS_SCHMITT;
+            if (eeprom->group1_slew == SLOW_SLEW)
+                output[0x0d] |= SLOW_SLEW;
+
+            set_ft232h_cbus(eeprom, output);
+
+            output[0x1e] = eeprom->chip;
+            fprintf(stderr,"FIXME: Build FT232H specific EEPROM settings\n");
+            break;
+        case TYPE_230X:
+            output[0x00] = 0x80; /* Actually, leave the default value */
+            output[0x0a] = 0x08; /* Enable USB Serial Number */
+            /*FIXME: Make DBUS & CBUS Control configurable*/
+            output[0x0c] = 0;    /* DBUS drive 4mA, CBUS drive 4 mA like factory default */
+            for (j = 0; j <= 6; j++)
+            {
+                output[0x1a + j] = eeprom->cbus_function[j];
+            }
+            output[0x0b] = eeprom->invert;
+            break;
+    }
+
+    /* First address without use */
+    free_start = 0;
+    switch (ftdi->type)
+    {
+        case TYPE_230X:
+            free_start += 2;
+        case TYPE_232H:
+            free_start += 6;
+        case TYPE_2232H:
+        case TYPE_4232H:
+            free_start += 2;
+        case TYPE_R:
+            free_start += 2;
+        case TYPE_2232C:
+            free_start++;
+        case TYPE_AM:
+        case TYPE_BM:
+            free_start += 0x14;
+    }
+
+    /* Arbitrary user data */
+    if (eeprom->user_data && eeprom->user_data_size >= 0)
+    {
+        if (eeprom->user_data_addr < free_start)
+            fprintf(stderr,"Warning, user data starts inside the generated data!\n");
+        if (eeprom->user_data_addr + eeprom->user_data_size >= free_end)
+            fprintf(stderr,"Warning, user data overlaps the strings area!\n");
+        if (eeprom->user_data_addr + eeprom->user_data_size > eeprom->size)
+            ftdi_error_return(-1,"eeprom size exceeded");
+        memcpy(output + eeprom->user_data_addr, eeprom->user_data, eeprom->user_data_size);
     }
 
     // calculate checksum
@@ -2641,9 +3229,23 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
     for (i = 0; i < eeprom->size/2-1; i++)
     {
-        value = output[i*2];
-        value += output[(i*2)+1] << 8;
-
+        if ((ftdi->type == TYPE_230X) && (i == 0x12))
+        {
+            /* FT230X has a user section in the MTP which is not part of the checksum */
+            i = 0x40;
+        }
+        if ((ftdi->type == TYPE_230X) && (i >=  0x40) && (i < 0x50)) {
+            uint16_t data;
+            if (ftdi_read_eeprom_location(ftdi, i, &data)) {
+                fprintf(stderr, "Reading Factory Configuration Data failed\n");
+                i = 0x50;
+            }
+            value = data;
+        }
+        else {
+            value = output[i*2];
+            value += output[(i*2)+1] << 8;
+        }
         checksum = value^checksum;
         checksum = (checksum << 1) | (checksum >> 15);
     }
@@ -2651,12 +3253,49 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     output[eeprom->size-2] = checksum;
     output[eeprom->size-1] = checksum >> 8;
 
+    eeprom->initialized_for_connected_device = 1;
     return user_area_size;
 }
+/* Decode the encoded EEPROM field for the FTDI Mode into a value for the abstracted
+ * EEPROM structure
+ *
+ * FTD2XX doesn't allow to set multiple bits in the interface mode bitfield, and so do we
+ */
+static unsigned char bit2type(unsigned char bits)
+{
+    switch (bits)
+    {
+        case   0: return CHANNEL_IS_UART;
+        case   1: return CHANNEL_IS_FIFO;
+        case   2: return CHANNEL_IS_OPTO;
+        case   4: return CHANNEL_IS_CPU;
+        case   8: return CHANNEL_IS_FT1284;
+        default:
+            fprintf(stderr," Unexpected value %d for Hardware Interface type\n",
+                    bits);
+    }
+    return 0;
+}
+/* Decode 230X / 232R type chips invert bits
+ * Prints directly to stdout.
+*/
+static void print_inverted_bits(int invert)
+{
+    const char *r_bits[] = {"TXD","RXD","RTS","CTS","DTR","DSR","DCD","RI"};
+    int i;
+
+    fprintf(stdout,"Inverted bits:");
+    for (i=0; i<8; i++)
+        if ((invert & (1<<i)) == (1<<i))
+            fprintf(stdout," %s",r_bits[i]);
 
+    fprintf(stdout,"\n");
+}
 /**
    Decode binary EEPROM image into an ftdi_eeprom structure.
 
+   For FT-X devices use AN_201 FT-X MTP memory Configuration to decode.
+
    \param ftdi pointer to ftdi_context
    \param verbose Decode EEPROM on stdout
 
@@ -2668,13 +3307,12 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 */
 int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 {
-    unsigned char i, j;
+    int i, j;
     unsigned short checksum, eeprom_checksum, value;
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
     int eeprom_size;
     struct ftdi_eeprom *eeprom;
-    unsigned char *buf = ftdi->eeprom->buf;
-    int release;
+    unsigned char *buf = NULL;
 
     if (ftdi == NULL)
         ftdi_error_return(-1,"No context");
@@ -2683,6 +3321,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 
     eeprom = ftdi->eeprom;
     eeprom_size = eeprom->size;
+    buf = ftdi->eeprom->buf;
 
     // Addr 02: Vendor ID
     eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8);
@@ -2690,7 +3329,8 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     // Addr 04: Product ID
     eeprom->product_id = buf[0x04] + (buf[0x05] << 8);
 
-    release = buf[0x06] + (buf[0x07]<<8);
+    // Addr 06: Device release number
+    eeprom->release_number = buf[0x06] + (buf[0x07]<<8);
 
     // Addr 08: Config descriptor
     // Bit 7: always 1
@@ -2700,7 +3340,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     eeprom->remote_wakeup = buf[0x08] & 0x20;
 
     // Addr 09: Max power consumption: max power = value * 2 mA
-    eeprom->max_power = buf[0x09];
+    eeprom->max_power = MAX_POWER_MILLIAMP_PER_UNIT * buf[0x09];
 
     // Addr 0A: Chip configuration
     // Bit 7: 0 - reserved
@@ -2715,8 +3355,8 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     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;
+    eeprom->use_serial         = !!(buf[0x0A] & USE_SERIAL_NUM);
+    eeprom->use_usb_version    = !!(buf[0x0A] & USE_USB_VERSION_BIT);
 
     // Addr 0C: USB version low byte when 0x0A
     // Addr 0D: USB version high byte when 0x0A
@@ -2729,12 +3369,12 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
         free(eeprom->manufacturer);
     if (manufacturer_size > 0)
     {
-        eeprom->manufacturer = malloc(manufacturer_size);
+        eeprom->manufacturer = (char *)malloc(manufacturer_size);
         if (eeprom->manufacturer)
         {
             // Decode manufacturer
             i = buf[0x0E] & (eeprom_size -1); // offset
-            for (j=0;j<manufacturer_size-1;j++)
+            for (j=0; j<manufacturer_size-1; j++)
             {
                 eeprom->manufacturer[j] = buf[2*j+i+2];
             }
@@ -2750,12 +3390,12 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     product_size = buf[0x11]/2;
     if (product_size > 0)
     {
-        eeprom->product = malloc(product_size);
+        eeprom->product = (char *)malloc(product_size);
         if (eeprom->product)
         {
             // Decode product name
             i = buf[0x10] & (eeprom_size -1); // offset
-            for (j=0;j<product_size-1;j++)
+            for (j=0; j<product_size-1; j++)
             {
                 eeprom->product[j] = buf[2*j+i+2];
             }
@@ -2771,12 +3411,12 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     serial_size = buf[0x13]/2;
     if (serial_size > 0)
     {
-        eeprom->serial = malloc(serial_size);
+        eeprom->serial = (char *)malloc(serial_size);
         if (eeprom->serial)
         {
             // Decode serial
             i = buf[0x12] & (eeprom_size -1); // offset
-            for (j=0;j<serial_size-1;j++)
+            for (j=0; j<serial_size-1; j++)
             {
                 eeprom->serial[j] = buf[2*j+i+2];
             }
@@ -2790,6 +3430,11 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 
     for (i = 0; i < eeprom_size/2-1; i++)
     {
+        if ((ftdi->type == TYPE_230X) && (i == 0x12))
+        {
+            /* FT230X has a user section in the MTP which is not part of the checksum */
+            i = 0x40;
+        }
         value = buf[i*2];
         value += buf[(i*2)+1] << 8;
 
@@ -2812,7 +3457,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     }
     else if (ftdi->type == TYPE_2232C)
     {
-        eeprom->channel_a_type   = buf[0x00] & 0x7;
+        eeprom->channel_a_type   = bit2type(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;
@@ -2823,8 +3468,9 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     else if (ftdi->type == TYPE_R)
     {
         /* TYPE_R flags D2XX, not VCP as all others*/
-        eeprom->channel_a_driver = (~buf[0x00]) & DRIVER_VCP;
+        eeprom->channel_a_driver = ~buf[0x00] & DRIVER_VCP;
         eeprom->high_current     = buf[0x00] & HIGH_CURRENT_DRIVE_R;
+        eeprom->external_oscillator = buf[0x00] & 0x02;
         if ( (buf[0x01]&0x40) != 0x40)
             fprintf(stderr,
                     "TYPE_R EEPROM byte[0x01] Bit 6 unexpected Endpoint size."
@@ -2845,15 +3491,26 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
         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))
+    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->channel_a_type   = bit2type(buf[0x00] & 0x7);
+            eeprom->channel_b_type   = bit2type(buf[0x01] & 0x7);
             eeprom->suspend_dbus7    = buf[0x01] & SUSPEND_DBUS7_BIT;
+        }
+        else
+        {
+            eeprom->channel_c_driver = (buf[0x00] >> 4) & DRIVER_VCP;
+            eeprom->channel_d_driver = (buf[0x01] >> 4) & DRIVER_VCP;
+            eeprom->channel_a_rs485enable = buf[0x0b] & (CHANNEL_IS_RS485 << 0);
+            eeprom->channel_b_rs485enable = buf[0x0b] & (CHANNEL_IS_RS485 << 1);
+            eeprom->channel_c_rs485enable = buf[0x0b] & (CHANNEL_IS_RS485 << 2);
+            eeprom->channel_d_rs485enable = buf[0x0b] & (CHANNEL_IS_RS485 << 3);
+        }
 
         eeprom->chip = buf[0x18];
         eeprom->group0_drive   =  buf[0x0c]       & DRIVE_16MA;
@@ -2869,18 +3526,56 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
         eeprom->group3_schmitt = (buf[0x0d] >> 4) & IS_SCHMITT;
         eeprom->group3_slew    = (buf[0x0d] >> 4) & SLOW_SLEW;
     }
+    else if (ftdi->type == TYPE_232H)
+    {
+        eeprom->channel_a_type   = buf[0x00] & 0xf;
+        eeprom->channel_a_driver = (buf[0x00] & DRIVER_VCPH)?DRIVER_VCP:0;
+        eeprom->clock_polarity =  buf[0x01]       & FT1284_CLK_IDLE_STATE;
+        eeprom->data_order     =  buf[0x01]       & FT1284_DATA_LSB;
+        eeprom->flow_control   =  buf[0x01]       & FT1284_FLOW_CONTROL;
+        eeprom->powersave      =  buf[0x01]       & POWER_SAVE_DISABLE_H;
+        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[0x0d]       & DRIVE_16MA;
+        eeprom->group1_schmitt =  buf[0x0d]       & IS_SCHMITT;
+        eeprom->group1_slew    =  buf[0x0d]       & SLOW_SLEW;
+
+        for(i=0; i<5; i++)
+        {
+            eeprom->cbus_function[2*i  ] =  buf[0x18+i] & 0x0f;
+            eeprom->cbus_function[2*i+1] = (buf[0x18+i] >> 4) & 0x0f;
+        }
+        eeprom->chip = buf[0x1e];
+        /*FIXME: Decipher more values*/
+    }
+    else if (ftdi->type == TYPE_230X)
+    {
+        for(i=0; i<4; i++)
+        {
+            eeprom->cbus_function[i] =  buf[0x1a + i] & 0xFF;
+        }
+        eeprom->group0_drive   =  buf[0x0c]       & 0x03;
+        eeprom->group0_schmitt =  buf[0x0c]       & IS_SCHMITT;
+        eeprom->group0_slew    =  buf[0x0c]       & SLOW_SLEW;
+        eeprom->group1_drive   = (buf[0x0c] >> 4) & 0x03;
+        eeprom->group1_schmitt = (buf[0x0c] >> 4) & IS_SCHMITT;
+        eeprom->group1_slew    = (buf[0x0c] >> 4) & SLOW_SLEW;
+
+        eeprom->invert = buf[0xb];
+    }
 
     if (verbose)
     {
-        char *channel_mode[] = {"UART","245","CPU", "unknown", "OPTO"};
+        const char *channel_mode[] = {"UART", "FIFO", "CPU", "OPTO", "FT1284"};
         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);
+        fprintf(stdout, "Release: 0x%04x\n",eeprom->release_number);
 
         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,
+            fprintf(stdout, "Bus Powered: %3d mA%s", eeprom->max_power,
                     (eeprom->remote_wakeup)?" USB Remote Wake Up\n":"\n");
         if (eeprom->manufacturer)
             fprintf(stdout, "Manufacturer: %s\n",eeprom->manufacturer);
@@ -2889,14 +3584,21 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
         if (eeprom->serial)
             fprintf(stdout, "Serial:       %s\n",eeprom->serial);
         fprintf(stdout,     "Checksum      : %04x\n", checksum);
-        if (ftdi->type == TYPE_R)
+        if (ftdi->type == TYPE_R) {
             fprintf(stdout,     "Internal EEPROM\n");
+            fprintf(stdout,"Oscillator: %s\n", eeprom->external_oscillator?"External":"Internal");
+        }
         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->powersave)
+        {
+            if(ftdi->type >= TYPE_232H)
+                fprintf(stdout,"Enter low power state on ACBUS7\n");
+        }
         if (eeprom->remote_wakeup)
             fprintf(stdout, "Enable Remote Wake Up\n");
         fprintf(stdout, "PNP: %d\n",(eeprom->is_not_pnp)?0:1);
@@ -2905,13 +3607,20 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
                     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))
+        if (ftdi->type == TYPE_232H)
+        {
+            fprintf(stdout,"FT1284 Mode Clock is idle %s, %s first, %sFlow Control\n",
+                    (eeprom->clock_polarity)?"HIGH":"LOW",
+                    (eeprom->data_order)?"LSB":"MSB",
+                    (eeprom->flow_control)?"":"No ");
+        }
+        if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H))
             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)
+                eeprom->use_usb_version)
             fprintf(stdout,"Use explicit USB Version %04x\n",eeprom->usb_version);
 
         if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H))
@@ -2937,37 +3646,78 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
                     (eeprom->group3_schmitt)?" Schmitt Input":"",
                     (eeprom->group3_slew)?" Slow Slew":"");
         }
+        else if (ftdi->type == TYPE_232H)
+        {
+            const char *cbush_mux[] = {"TRISTATE","TXLED","RXLED", "TXRXLED","PWREN",
+                                 "SLEEP","DRIVE_0","DRIVE_1","IOMODE","TXDEN",
+                                 "CLK30","CLK15","CLK7_5"
+                                };
+            fprintf(stdout,"ACBUS has %d mA drive%s%s\n",
+                    (eeprom->group0_drive+1) *4,
+                    (eeprom->group0_schmitt)?" Schmitt Input":"",
+                    (eeprom->group0_slew)?" Slow Slew":"");
+            fprintf(stdout,"ADBUS has %d mA drive%s%s\n",
+                    (eeprom->group1_drive+1) *4,
+                    (eeprom->group1_schmitt)?" Schmitt Input":"",
+                    (eeprom->group1_slew)?" Slow Slew":"");
+            for (i=0; i<10; i++)
+            {
+                if (eeprom->cbus_function[i]<= CBUSH_CLK7_5 )
+                    fprintf(stdout,"C%d Function: %s\n", i,
+                            cbush_mux[eeprom->cbus_function[i]]);
+            }
+        }
+        else if (ftdi->type == TYPE_230X)
+        {
+            const char *cbusx_mux[] = {"TRISTATE","TXLED","RXLED", "TXRXLED","PWREN",
+                                 "SLEEP","DRIVE_0","DRIVE_1","IOMODE","TXDEN",
+                                 "CLK24","CLK12","CLK6","BAT_DETECT","BAT_DETECT#",
+                                 "I2C_TXE#", "I2C_RXF#", "VBUS_SENSE", "BB_WR#",
+                                 "BBRD#", "TIME_STAMP", "AWAKE#",
+                                };
+            fprintf(stdout,"DBUS has %d mA drive%s%s\n",
+                    (eeprom->group0_drive+1) *4,
+                    (eeprom->group0_schmitt)?" Schmitt Input":"",
+                    (eeprom->group0_slew)?" Slow Slew":"");
+            fprintf(stdout,"CBUS has %d mA drive%s%s\n",
+                    (eeprom->group1_drive+1) *4,
+                    (eeprom->group1_schmitt)?" Schmitt Input":"",
+                    (eeprom->group1_slew)?" Slow Slew":"");
+            for (i=0; i<4; i++)
+            {
+                if (eeprom->cbus_function[i]<= CBUSX_AWAKE)
+                    fprintf(stdout,"CBUS%d Function: %s\n", i, cbusx_mux[eeprom->cbus_function[i]]);
+            }
+
+            if (eeprom->invert)
+                print_inverted_bits(eeprom->invert);
+        }
+
         if (ftdi->type == TYPE_R)
         {
-            char *cbus_mux[] = {"TXDEN","PWREN","RXLED", "TXLED","TX+RXLED",
+            const 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"};
+            const 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");
-            }
+                print_inverted_bits(eeprom->invert);
+
             for (i=0; i<5; i++)
             {
-                if (eeprom->cbus_function[i]<CBUS_BB)
+                if (eeprom->cbus_function[i]<=CBUS_BB_RD)
                     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;
+                    if (i < 4)
+                        /* Running MPROG show that C0..3 have fixed function Synchronous
+                           Bit Bang mode */
+                        fprintf(stdout,"C%d BB Function: %s\n", i,
+                                cbus_BB[i]);
+                    else
+                        fprintf(stdout, "Unknown CBUS mode. Might be special mode?\n");
                 }
             }
         }
@@ -2995,6 +3745,9 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case PRODUCT_ID:
             *value = ftdi->eeprom->product_id;
             break;
+        case RELEASE_NUMBER:
+            *value = ftdi->eeprom->release_number;
+            break;
         case SELF_POWERED:
             *value = ftdi->eeprom->self_powered;
             break;
@@ -3010,6 +3763,9 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case IN_IS_ISOCHRONOUS:
             *value = ftdi->eeprom->in_is_isochronous;
             break;
+        case OUT_IS_ISOCHRONOUS:
+            *value = ftdi->eeprom->out_is_isochronous;
+            break;
         case SUSPEND_PULL_DOWNS:
             *value = ftdi->eeprom->suspend_pull_downs;
             break;
@@ -3019,6 +3775,9 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case USB_VERSION:
             *value = ftdi->eeprom->usb_version;
             break;
+        case USE_USB_VERSION:
+            *value = ftdi->eeprom->use_usb_version;
+            break;
         case MAX_POWER:
             *value = ftdi->eeprom->max_power;
             break;
@@ -3034,6 +3793,24 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case CHANNEL_B_DRIVER:
             *value = ftdi->eeprom->channel_b_driver;
             break;
+        case CHANNEL_C_DRIVER:
+            *value = ftdi->eeprom->channel_c_driver;
+            break;
+        case CHANNEL_D_DRIVER:
+            *value = ftdi->eeprom->channel_d_driver;
+            break;
+        case CHANNEL_A_RS485:
+            *value = ftdi->eeprom->channel_a_rs485enable;
+            break;
+        case CHANNEL_B_RS485:
+            *value = ftdi->eeprom->channel_b_rs485enable;
+            break;
+        case CHANNEL_C_RS485:
+            *value = ftdi->eeprom->channel_c_rs485enable;
+            break;
+        case CHANNEL_D_RS485:
+            *value = ftdi->eeprom->channel_d_rs485enable;
+            break;
         case CBUS_FUNCTION_0:
             *value = ftdi->eeprom->cbus_function[0];
             break;
@@ -3049,6 +3826,21 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case CBUS_FUNCTION_4:
             *value = ftdi->eeprom->cbus_function[4];
             break;
+        case CBUS_FUNCTION_5:
+            *value = ftdi->eeprom->cbus_function[5];
+            break;
+        case CBUS_FUNCTION_6:
+            *value = ftdi->eeprom->cbus_function[6];
+            break;
+        case CBUS_FUNCTION_7:
+            *value = ftdi->eeprom->cbus_function[7];
+            break;
+        case CBUS_FUNCTION_8:
+            *value = ftdi->eeprom->cbus_function[8];
+            break;
+        case CBUS_FUNCTION_9:
+            *value = ftdi->eeprom->cbus_function[9];
+            break;
         case HIGH_CURRENT:
             *value = ftdi->eeprom->high_current;
             break;
@@ -3097,12 +3889,27 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case GROUP3_SLEW:
             *value = ftdi->eeprom->group3_slew;
             break;
+        case POWER_SAVE:
+            *value = ftdi->eeprom->powersave;
+            break;
+        case CLOCK_POLARITY:
+            *value = ftdi->eeprom->clock_polarity;
+            break;
+        case DATA_ORDER:
+            *value = ftdi->eeprom->data_order;
+            break;
+        case FLOW_CONTROL:
+            *value = ftdi->eeprom->flow_control;
+            break;
         case CHIP_TYPE:
             *value = ftdi->eeprom->chip;
             break;
         case CHIP_SIZE:
             *value = ftdi->eeprom->size;
             break;
+        case EXTERNAL_OSCILLATOR:
+            *value = ftdi->eeprom->external_oscillator;
+            break;
         default:
             ftdi_error_return(-1, "Request for unknown EEPROM value");
     }
@@ -3131,6 +3938,9 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case PRODUCT_ID:
             ftdi->eeprom->product_id = value;
             break;
+        case RELEASE_NUMBER:
+            ftdi->eeprom->release_number = value;
+            break;
         case SELF_POWERED:
             ftdi->eeprom->self_powered = value;
             break;
@@ -3146,6 +3956,9 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case IN_IS_ISOCHRONOUS:
             ftdi->eeprom->in_is_isochronous = value;
             break;
+        case OUT_IS_ISOCHRONOUS:
+            ftdi->eeprom->out_is_isochronous = value;
+            break;
         case SUSPEND_PULL_DOWNS:
             ftdi->eeprom->suspend_pull_downs = value;
             break;
@@ -3155,6 +3968,9 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case USB_VERSION:
             ftdi->eeprom->usb_version = value;
             break;
+        case USE_USB_VERSION:
+            ftdi->eeprom->use_usb_version = value;
+            break;
         case MAX_POWER:
             ftdi->eeprom->max_power = value;
             break;
@@ -3170,6 +3986,24 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case CHANNEL_B_DRIVER:
             ftdi->eeprom->channel_b_driver = value;
             break;
+        case CHANNEL_C_DRIVER:
+            ftdi->eeprom->channel_c_driver = value;
+            break;
+        case CHANNEL_D_DRIVER:
+            ftdi->eeprom->channel_d_driver = value;
+            break;
+        case CHANNEL_A_RS485:
+            ftdi->eeprom->channel_a_rs485enable = value;
+            break;
+        case CHANNEL_B_RS485:
+            ftdi->eeprom->channel_b_rs485enable = value;
+            break;
+        case CHANNEL_C_RS485:
+            ftdi->eeprom->channel_c_rs485enable = value;
+            break;
+        case CHANNEL_D_RS485:
+            ftdi->eeprom->channel_d_rs485enable = value;
+            break;
         case CBUS_FUNCTION_0:
             ftdi->eeprom->cbus_function[0] = value;
             break;
@@ -3185,6 +4019,21 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case CBUS_FUNCTION_4:
             ftdi->eeprom->cbus_function[4] = value;
             break;
+        case CBUS_FUNCTION_5:
+            ftdi->eeprom->cbus_function[5] = value;
+            break;
+        case CBUS_FUNCTION_6:
+            ftdi->eeprom->cbus_function[6] = value;
+            break;
+        case CBUS_FUNCTION_7:
+            ftdi->eeprom->cbus_function[7] = value;
+            break;
+        case CBUS_FUNCTION_8:
+            ftdi->eeprom->cbus_function[8] = value;
+            break;
+        case CBUS_FUNCTION_9:
+            ftdi->eeprom->cbus_function[9] = value;
+            break;
         case HIGH_CURRENT:
             ftdi->eeprom->high_current = value;
             break;
@@ -3236,11 +4085,32 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case CHIP_TYPE:
             ftdi->eeprom->chip = value;
             break;
+        case POWER_SAVE:
+            ftdi->eeprom->powersave = value;
+            break;
+        case CLOCK_POLARITY:
+            ftdi->eeprom->clock_polarity = value;
+            break;
+        case DATA_ORDER:
+            ftdi->eeprom->data_order = value;
+            break;
+        case FLOW_CONTROL:
+            ftdi->eeprom->flow_control = value;
+            break;
         case CHIP_SIZE:
             ftdi_error_return(-2, "EEPROM Value can't be changed");
+            break;
+        case EXTERNAL_OSCILLATOR:
+            ftdi->eeprom->external_oscillator = value;
+            break;
+        case USER_DATA_ADDR:
+            ftdi->eeprom->user_data_addr = value;
+            break;
+
         default :
             ftdi_error_return(-1, "Request to unknown EEPROM value");
     }
+    ftdi->eeprom->initialized_for_connected_device = 0;
     return 0;
 }
 
@@ -3271,6 +4141,48 @@ int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size
     return 0;
 }
 
+/** Set the EEPROM content from the user-supplied prefilled buffer
+
+    \param ftdi pointer to ftdi_context
+    \param buf buffer to read EEPROM content
+    \param size Size of buffer
+
+    \retval 0: All fine
+    \retval -1: struct ftdi_context or ftdi_eeprom or buf missing
+*/
+int ftdi_set_eeprom_buf(struct ftdi_context *ftdi, const unsigned char * buf, int size)
+{
+    if (!ftdi || !(ftdi->eeprom) || !buf)
+        ftdi_error_return(-1, "No appropriate structure");
+
+    // Only copy up to FTDI_MAX_EEPROM_SIZE bytes
+    if (size > FTDI_MAX_EEPROM_SIZE)
+        size = FTDI_MAX_EEPROM_SIZE;
+
+    memcpy(ftdi->eeprom->buf, buf, size);
+
+    return 0;
+}
+
+/** Set the EEPROM user data content from the user-supplied prefilled buffer
+
+    \param ftdi pointer to ftdi_context
+    \param buf buffer to read EEPROM user data content
+    \param size Size of buffer
+
+    \retval 0: All fine
+    \retval -1: struct ftdi_context or ftdi_eeprom or buf missing
+*/
+int ftdi_set_eeprom_user_data(struct ftdi_context *ftdi, const char * buf, int size)
+{
+    if (!ftdi || !(ftdi->eeprom) || !buf)
+        ftdi_error_return(-1, "No appropriate structure");
+
+    ftdi->eeprom->user_data_size = size;
+    ftdi->eeprom->user_data = buf;
+    return 0;
+}
+
 /**
     Read eeprom location
 
@@ -3284,12 +4196,16 @@ int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size
 */
 int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsigned short *eeprom_val)
 {
+    unsigned char buf[2];
+
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
 
-    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_EEPROM_REQUEST, 0, eeprom_addr, (unsigned char *)eeprom_val, 2, ftdi->usb_read_timeout) != 2)
+    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_EEPROM_REQUEST, 0, eeprom_addr, buf, 2, ftdi->usb_read_timeout) != 2)
         ftdi_error_return(-1, "reading eeprom failed");
 
+    *eeprom_val = (0xff & buf[0]) | (buf[1] << 8);
+
     return 0;
 }
 
@@ -3422,12 +4338,15 @@ int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr,
         case TYPE_4232H:
             chip_type_location = 0x18;
             break;
+        case TYPE_232H:
+            chip_type_location = 0x1e;
+            break;
         default:
             ftdi_error_return(-4, "Device can't access unprotected area");
     }
 
     if (ftdi_read_eeprom_location( ftdi, chip_type_location>>1, &chip_type))
-        ftdi_error_return(-5, "Reading failed failed");
+        ftdi_error_return(-5, "Reading failed");
     fprintf(stderr," loc 0x%04x val 0x%04x\n", chip_type_location,chip_type);
     if ((chip_type & 0xff) != 0x66)
     {
@@ -3450,6 +4369,7 @@ int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr,
     \retval  0: all fine
     \retval -1: read failed
     \retval -2: USB device unavailable
+    \retval -3: EEPROM not initialized for the connected device;
 */
 int ftdi_write_eeprom(struct ftdi_context *ftdi)
 {
@@ -3459,6 +4379,10 @@ int ftdi_write_eeprom(struct ftdi_context *ftdi)
 
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
+
+    if(ftdi->eeprom->initialized_for_connected_device == 0)
+        ftdi_error_return(-3, "EEPROM not initialized for the connected device");
+
     eeprom = ftdi->eeprom->buf;
 
     /* These commands were traced while running MProg */
@@ -3471,6 +4395,11 @@ int ftdi_write_eeprom(struct ftdi_context *ftdi)
 
     for (i = 0; i < ftdi->eeprom->size/2; i++)
     {
+        /* Do not try to write to reserved area */
+        if ((ftdi->type == TYPE_230X) && (i == 0x40))
+        {
+            i = 0x50;
+        }
         usb_val = eeprom[i*2];
         usb_val += eeprom[(i*2)+1] << 8;
         if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
@@ -3503,7 +4432,7 @@ int ftdi_erase_eeprom(struct ftdi_context *ftdi)
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
 
-    if (ftdi->type == TYPE_R)
+    if ((ftdi->type == TYPE_R) || (ftdi->type == TYPE_230X))
     {
         ftdi->eeprom->chip = 0;
         return 0;
@@ -3523,7 +4452,7 @@ int ftdi_erase_eeprom(struct ftdi_context *ftdi)
                                 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");
+        ftdi_error_return(-4, "Reading failed");
     if (eeprom_value == MAGIC)
     {
         ftdi->eeprom->chip = 0x46;
@@ -3531,13 +4460,13 @@ int ftdi_erase_eeprom(struct ftdi_context *ftdi)
     else
     {
         if (ftdi_read_eeprom_location( ftdi, 0x40, &eeprom_value))
-            ftdi_error_return(-4, "Reading failed failed");
+            ftdi_error_return(-4, "Reading 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");
+                ftdi_error_return(-4, "Reading failed");
             if (eeprom_value == MAGIC)
                 ftdi->eeprom->chip = 0x66;
             else
@@ -3559,7 +4488,7 @@ int ftdi_erase_eeprom(struct ftdi_context *ftdi)
 
     \retval Pointer to error string
 */
-char *ftdi_get_error_string (struct ftdi_context *ftdi)
+const char *ftdi_get_error_string (struct ftdi_context *ftdi)
 {
     if (ftdi == NULL)
         return "";