eeprom handling: Add support for arbitrary user data
[libftdi] / src / ftdi.c
index b613721..aa4b4ec 100644 (file)
@@ -2,7 +2,7 @@
                           ftdi.c  -  description
                              -------------------
     begin                : Fri Apr 4 2003
-    copyright            : (C) 2003-2010 by Intra2net AG
+    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);
 
@@ -61,8 +66,10 @@ 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;
+        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,14 +102,14 @@ 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;
 
-    ftdi->interface = 0;
-    ftdi->index = 0;
-    ftdi->in_ep = 0x02;
-    ftdi->out_ep = 0x81;
-    ftdi->bitbang_mode = 1; /* when bitbang is enabled this holds the number of the mode  */
+    if (libusb_init(&ftdi->usb_ctx) < 0)
+        ftdi_error_return(-3, "libusb_init() failed");
 
-    ftdi->error_str = NULL;
+    ftdi_set_interface(ftdi, INTERFACE_ANY);
+    ftdi->bitbang_mode = 1; /* when bitbang is enabled this holds the number of the mode  */
 
     if (eeprom == 0)
         ftdi_error_return(-2, "Can't malloc struct ftdi_eeprom");
@@ -144,17 +152,31 @@ 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:
         case INTERFACE_A:
-            /* ftdi_usb_open_desc cares to set the right index, depending on the found chip */
+            ftdi->interface = 0;
+            ftdi->index     = INTERFACE_A;
+            ftdi->in_ep     = 0x02;
+            ftdi->out_ep    = 0x81;
             break;
         case INTERFACE_B:
             ftdi->interface = 1;
@@ -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;
     }
@@ -366,8 +414,8 @@ int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct libusb_device * dev,
     if ((ftdi==NULL) || (dev==NULL))
         return -1;
 
-    if (libusb_open(dev, &ftdi->usb_dev) < 0)
-        ftdi_error_return(-4, "libusb_open() failed");
+    if (ftdi->usb_dev == NULL && libusb_open(dev, &ftdi->usb_dev) < 0)
+            ftdi_error_return(-4, "libusb_open() failed");
 
     if (libusb_get_device_descriptor(dev, &desc) < 0)
         ftdi_error_return(-11, "libusb_get_device_descriptor() failed");
@@ -423,7 +471,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 +517,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 +546,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");
@@ -511,7 +562,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!");
             }
@@ -525,7 +576,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!");
         }
@@ -544,7 +595,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;
@@ -556,19 +607,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;
-
-    // Set default interface on dual/quad type chips
-    switch(ftdi->type)
-    {
-        case TYPE_2232C:
-        case TYPE_2232H:
-        case TYPE_4232H:
-            if (!ftdi->index)
-                ftdi->index = INTERFACE_A;
-            break;
-        default:
-            break;
-    }
+    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);
@@ -614,7 +656,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
 */
@@ -649,16 +690,13 @@ 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;
     char string[256];
     int i = 0;
 
-    if (libusb_init(&ftdi->usb_ctx) < 0)
-        ftdi_error_return(-11, "libusb_init() failed");
-
     if (ftdi == NULL)
         ftdi_error_return(-11, "ftdi context invalid");
 
@@ -682,12 +720,12 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
             {
                 if (libusb_get_string_descriptor_ascii(ftdi->usb_dev, desc.iProduct, (unsigned char *)string, sizeof(string)) < 0)
                 {
-                    libusb_close (ftdi->usb_dev);
+                    ftdi_usb_close_internal (ftdi);
                     ftdi_error_return_free_device_list(-8, "unable to fetch product description", devs);
                 }
                 if (strncmp(string, description, sizeof(string)) != 0)
                 {
-                    libusb_close (ftdi->usb_dev);
+                    ftdi_usb_close_internal (ftdi);
                     continue;
                 }
             }
@@ -707,11 +745,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);
@@ -737,7 +775,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
@@ -762,24 +799,21 @@ 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");
-
-       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);
@@ -966,34 +1000,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;
@@ -1011,11 +1041,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
@@ -1023,23 +1048,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 up to supported fraction (AM only)
-                try_divisor += am_adjust_up[try_divisor & 7];
-                if (try_divisor > 0x1FFF8)
-                {
-                    // Round down to maximum supported divisor value (for AM)
-                    try_divisor = 0x1FFF8;
-                }
-            }
-            else
-            {
-                if (try_divisor > 0x1FFFF)
-                {
-                    // Round down to maximum supported divisor value (for BM)
-                    try_divisor = 0x1FFFF;
-                }
+                // Round down to maximum supported divisor value (for AM)
+                try_divisor = 0x1FFF8;
             }
         }
         // Get estimated baud rate (to nearest integer)
@@ -1067,19 +1081,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
+        *encoded_divisor = 0;    // 3000000 baud
     }
-    else if (encoded_divisor == 0x4001)
+    else if (*encoded_divisor == 0x4001)
     {
-        encoded_divisor = 1;    // 2000000 baud (BM only)
+        *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 = 1;
+        best_baud = clk/(clk_div + clk_div/2);
+    }
+    else if (baudrate >=  clk/(2*clk_div))
+    {
+        *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 ))
+    {
+        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;
@@ -1093,6 +1215,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
@@ -1238,7 +1370,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;
@@ -1253,7 +1385,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;
@@ -1262,7 +1394,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;
@@ -1286,9 +1418,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,
@@ -1297,7 +1429,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)
@@ -1344,13 +1476,13 @@ static void ftdi_read_data_cb(struct libusb_transfer *transfer)
 }
 
 
-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;
-    
+
     tc->offset += transfer->actual_length;
-    
+
     if (tc->offset == tc->size)
     {
         tc->completed = 1;
@@ -1389,19 +1521,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;
@@ -1409,10 +1544,10 @@ struct ftdi_transfer_control *ftdi_write_data_submit(struct ftdi_context *ftdi,
     tc->size = size;
     tc->offset = 0;
 
-    if (size < ftdi->writebuffer_chunksize)
-      write_size = size;
+    if (size < (int)ftdi->writebuffer_chunksize)
+        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,
@@ -1423,8 +1558,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;
@@ -1463,7 +1597,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);
 
@@ -1627,7 +1761,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);
 
@@ -1784,21 +1918,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;
 
@@ -1806,15 +1938,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;
 }
 
@@ -1839,34 +1968,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.
@@ -1995,7 +2096,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;
 }
@@ -2179,7 +2280,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
@@ -2188,9 +2289,10 @@ 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)
+                             char * product, char * serial)
 {
     struct ftdi_eeprom *eeprom;
 
@@ -2198,18 +2300,28 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
         ftdi_error_return(-1, "No struct ftdi_context");
 
     if (ftdi->eeprom == NULL)
-        ftdi_error_return(-2,"No struct ftdi_eeprom"); 
+        ftdi_error_return(-2,"No struct ftdi_eeprom");
 
     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;
-    if((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM) ||
-       (ftdi->type == TYPE_R))
+    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
@@ -2229,11 +2341,32 @@ 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);
         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 = malloc(strlen(default_product) +1);
+        if (eeprom->product)
+            strcpy(eeprom->product, default_product);
+    }
 
     if (eeprom->serial)
         free (eeprom->serial);
@@ -2245,8 +2378,7 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
             strcpy(eeprom->serial, serial);
     }
 
-
-    if(ftdi->type == TYPE_R)
+    if (ftdi->type == TYPE_R)
     {
         eeprom->max_power = 90;
         eeprom->size = 0x80;
@@ -2256,8 +2388,159 @@ 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 = malloc(strlen(manufacturer)+1);
+        if (eeprom->manufacturer)
+            strcpy(eeprom->manufacturer, manufacturer);
+    }
+
+    if(product)
+    {
+        if (eeprom->product)
+            free (eeprom->product);
+        eeprom->product = malloc(strlen(product)+1);
+        if (eeprom->product)
+            strcpy(eeprom->product, product);
+    }
+
+    if (serial)
+    {
+        if (eeprom->serial)
+            free (eeprom->serial);
+        eeprom->serial = 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_230X: /* FT230X is only UART */
+        default: return 0;
+    }
     return 0;
 }
 
@@ -2269,10 +2552,10 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
 
     \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_context *ftdi)
@@ -2280,7 +2563,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;
 
@@ -2292,13 +2575,16 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     eeprom= ftdi->eeprom;
     output = eeprom->buf;
 
-    if(eeprom->chip == -1)
-        ftdi_error_return(-5,"No connected EEPROM or EEPROM type unknown");
+    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);
@@ -2312,18 +2598,25 @@ 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:
-           user_area_size = 88;     // four extra config bytes + 4 bytes PnP stuff
-           break;
+            user_area_size = 90;     // two extra config bytes and 4 bytes PnP stuff
+            break;
+        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;
+            user_area_size = 86;
+            break;
+        case TYPE_232H:
+            user_area_size = 80;
+            break;
+        default:
+            user_area_size = 0;
+            break;
     }
     user_area_size  -= (manufacturer_size + product_size + serial_size) * 2;
 
@@ -2331,7 +2624,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
 
@@ -2344,30 +2647,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
@@ -2375,31 +2656,31 @@ 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
         // Bit 6: 0 - reserved
         // Bit 5: 0 - reserved
-        // Bit 4: 1 - Change USB version 
+        // 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)
+        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;
     }
@@ -2407,22 +2688,29 @@ 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)
-    {
-    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;
+    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;
+            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
@@ -2473,9 +2761,9 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
     output[0x13] = serial_size*2 + 2;
 
-    if(ftdi->type > TYPE_AM) /* use_serial not used in AM devices */
+    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;
@@ -2483,155 +2771,338 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
     /* Bytes and Bits specific to (some) types
        Write linear, as this allows easier fixing*/
-    switch(ftdi->type)
-    {
-    case TYPE_AM:
-        break;
-    case TYPE_BM:
-        output[0x0C] = eeprom->usb_version & 0xff;
-        output[0x0D] = (eeprom->usb_version>>8) & 0xff;
-        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;
+    switch (ftdi->type)
+    {
+        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)
+                output[0x0A] |= USE_USB_VERSION_BIT;
+            else
+                output[0x0A] &= ~USE_USB_VERSION_BIT;
 
-        if ( eeprom->high_current_a == HIGH_CURRENT_DRIVE)
-            output[0x00] |= HIGH_CURRENT_DRIVE;
-        else
-            output[0x00] &= ~HIGH_CURRENT_DRIVE;
+            break;
+        case TYPE_2232C:
 
-        output[0x01] = (eeprom->channel_b_type);
-        if ( eeprom->channel_b_driver == DRIVER_VCP)
-            output[0x01] |= DRIVER_VCP;
-        else
-            output[0x01] &= ~DRIVER_VCP;
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_2232C);
+            if ( eeprom->channel_a_driver == DRIVER_VCP)
+                output[0x00] |= DRIVER_VCP;
+            else
+                output[0x00] &= ~DRIVER_VCP;
 
-        if ( eeprom->high_current_b == HIGH_CURRENT_DRIVE)
-            output[0x01] |= HIGH_CURRENT_DRIVE;
-        else
-            output[0x01] &= ~HIGH_CURRENT_DRIVE;
+            if ( eeprom->high_current_a == HIGH_CURRENT_DRIVE)
+                output[0x00] |= HIGH_CURRENT_DRIVE;
+            else
+                output[0x00] &= ~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;
-        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;
+            output[0x01] = type2bit(eeprom->channel_b_type, TYPE_2232C);
+            if ( eeprom->channel_b_driver == DRIVER_VCP)
+                output[0x01] |= DRIVER_VCP;
+            else
+                output[0x01] &= ~DRIVER_VCP;
 
-        if(eeprom->cbus_function[0] > CBUS_BB)
-            output[0x14] = CBUS_TXLED;
-        else
-            output[0x14] = eeprom->cbus_function[0];
+            if ( eeprom->high_current_b == HIGH_CURRENT_DRIVE)
+                output[0x01] |= HIGH_CURRENT_DRIVE;
+            else
+                output[0x01] &= ~HIGH_CURRENT_DRIVE;
 
-        if(eeprom->cbus_function[1] > CBUS_BB)
-            output[0x14] |= CBUS_RXLED<<4;
-        else
-            output[0x14] |= eeprom->cbus_function[1]<<4;
+            if (eeprom->in_is_isochronous)
+                output[0x0A] |= 0x1;
+            else
+                output[0x0A] &= ~0x1;
+            if (eeprom->out_is_isochronous)
+                output[0x0A] |= 0x2;
+            else
+                output[0x0A] &= ~0x2;
+            if (eeprom->suspend_pull_downs)
+                output[0x0A] |= 0x4;
+            else
+                output[0x0A] &= ~0x4;
+            if (eeprom->use_usb_version)
+                output[0x0A] |= USE_USB_VERSION_BIT;
+            else
+                output[0x0A] &= ~USE_USB_VERSION_BIT;
 
-        if(eeprom->cbus_function[2] > CBUS_BB)
-            output[0x15] = CBUS_TXDEN;
-        else
-            output[0x15] = eeprom->cbus_function[2];
+            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;
+            if (eeprom->external_oscillator)
+                output[0x00] |= 0x02;
+            output[0x01] = 0x40; /* Hard coded Endpoint Size*/
+
+            if (eeprom->suspend_pull_downs)
+                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[3] > CBUS_BB)
-            output[0x15] |= CBUS_PWREN<<4;
-        else
-            output[0x15] |= eeprom->cbus_function[3]<<4;
+            if (eeprom->cbus_function[0] > CBUS_BB_RD)
+                output[0x14] = CBUS_TXLED;
+            else
+                output[0x14] = eeprom->cbus_function[0];
 
-        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;
+            if (eeprom->cbus_function[1] > CBUS_BB_RD)
+                output[0x14] |= CBUS_RXLED<<4;
+            else
+                output[0x14] |= eeprom->cbus_function[1]<<4;
 
-        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)
-            output[0x01] |= SUSPEND_DBUS7;
-        else
-            output[0x01] &= ~SUSPEND_DBUS7;
+            if (eeprom->cbus_function[2] > CBUS_BB_RD)
+                output[0x15] = CBUS_TXDEN;
+            else
+                output[0x15] = eeprom->cbus_function[2];
 
-        if (eeprom->suspend_pull_downs == 1)
-            output[0x0A] |= 0x4;
-        else
-            output[0x0A] &= ~0x4;
+            if (eeprom->cbus_function[3] > CBUS_BB_RD)
+                output[0x15] |= CBUS_PWREN<<4;
+            else
+                output[0x15] |= eeprom->cbus_function[3]<<4;
 
-        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;
+            if (eeprom->cbus_function[4] > CBUS_CLK6)
+                output[0x16] = CBUS_SLEEP;
+            else
+                output[0x16] = eeprom->cbus_function[4];
+            break;
+        case TYPE_2232H:
+            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] = type2bit(eeprom->channel_b_type, TYPE_2232H);
+            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;
 
-        output[0x18] = eeprom->chip;
+            if (eeprom->suspend_pull_downs)
+                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;
 
-        break;
-    case TYPE_4232H:
-        fprintf(stderr,"FIXME: Build FT4232H specific EEPROM settings\n");
+            output[0x18] = eeprom->chip;
+
+            break;
+        case TYPE_4232H:
+            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
@@ -2639,9 +3110,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);
     }
@@ -2649,15 +3134,52 @@ 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)
+{
+    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
-   
+
    \retval 0: all fine
    \retval -1: something went wrong
 
@@ -2666,21 +3188,21 @@ 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");
     if (ftdi->eeprom == NULL)
         ftdi_error_return(-1,"No eeprom structure");
+
     eeprom = ftdi->eeprom;
     eeprom_size = eeprom->size;
+    buf = ftdi->eeprom->buf;
 
     // Addr 02: Vendor ID
     eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8);
@@ -2688,7 +3210,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
@@ -2698,14 +3221,13 @@ 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
     // Bit 6: 0 - reserved
     // Bit 5: 0 - reserved
-    // Bit 4: 1 - Change USB version
-    //            Not seen on FT2232(D)
+    // 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
@@ -2714,30 +3236,26 @@ 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;
-    if(buf[0x0A]&0x10)
-        fprintf(stderr,
-                "EEPROM byte[0x0a] Bit 4 unexpected set. If this happened with the EEPROM\n"
-                "programmed by FTDI tools, please report to libftdi@developer.intra2net.com\n");
-
+    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 
+    // 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(eeprom->manufacturer)
+    if (eeprom->manufacturer)
         free(eeprom->manufacturer);
-    if (manufacturer_size > 0) 
+    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++)
+            for (j=0; j<manufacturer_size-1; j++)
             {
                 eeprom->manufacturer[j] = buf[2*j+i+2];
             }
@@ -2748,17 +3266,17 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 
     // Addr 10: Offset of the product string + 0x80, calculated later
     // Addr 11: Length of product string
-    if(eeprom->product)
+    if (eeprom->product)
         free(eeprom->product);
     product_size = buf[0x11]/2;
     if (product_size > 0)
     {
         eeprom->product = malloc(product_size);
-        if(eeprom->product)
+        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];
             }
@@ -2769,17 +3287,17 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 
     // Addr 12: Offset of the serial string + 0x80, calculated later
     // Addr 13: Length of serial string
-    if(eeprom->serial)
+    if (eeprom->serial)
         free(eeprom->serial);
     serial_size = buf[0x13]/2;
     if (serial_size > 0)
     {
         eeprom->serial = malloc(serial_size);
-        if(eeprom->serial)
+        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];
             }
@@ -2793,6 +3311,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;
 
@@ -2813,9 +3336,9 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     {
         eeprom->chip = -1;
     }
-    else if(ftdi->type == TYPE_2232C)
+    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,17 +3346,18 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
         eeprom->high_current_b   = buf[0x01] & HIGH_CURRENT_DRIVE;
         eeprom->chip = buf[0x14];
     }
-    else if(ftdi->type == TYPE_R)
+    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;
-        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->external_oscillator = buf[0x00] & 0x02;
+        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
@@ -2848,15 +3372,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->suspend_dbus7    = buf[0x01] & SUSPEND_DBUS7;
+        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;
@@ -2872,48 +3407,104 @@ 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;
     }
-    
-    if(verbose)
+    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"};
+        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)
+        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)
+        if (eeprom->manufacturer)
             fprintf(stdout, "Manufacturer: %s\n",eeprom->manufacturer);
-        if(eeprom->product)
+        if (eeprom->product)
             fprintf(stdout, "Product:      %s\n",eeprom->product);
-        if(eeprom->serial)
+        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)
+        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)
+        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);
         if (ftdi->type >= TYPE_2232C)
-            fprintf(stdout,"Channel A has Mode %s%s%s\n", 
+            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", 
+        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_2232H) || (ftdi->type == TYPE_4232H)) 
+        if (((ftdi->type == TYPE_BM) || (ftdi->type == TYPE_2232C)) &&
+                eeprom->use_usb_version)
+            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",
@@ -2936,31 +3527,79 @@ 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)
+        {
+            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)
+        {
+            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",
                                 "SLEEP","CLK48","CLK24","CLK12","CLK6",
-                                "IOMODE","BB_WR","BB_RD"};
+                                "IOMODE","BB_WR","BB_RD"
+                               };
             char *cbus_BB[] = {"RXF","TXE","RD", "WR"};
-            int i;
-            
-            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->invert)
+                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
-                    fprintf(stdout,"C%d BB Function: %s\n", i,
-                            cbus_BB[i]);
+                {
+                    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");
+                }
             }
         }
     }
@@ -2968,6 +3607,464 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 }
 
 /**
+   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 RELEASE_NUMBER:
+            *value = ftdi->eeprom->release_number;
+            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 OUT_IS_ISOCHRONOUS:
+            *value = ftdi->eeprom->out_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 USE_USB_VERSION:
+            *value = ftdi->eeprom->use_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 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;
+        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 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;
+        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 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");
+    }
+    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 RELEASE_NUMBER:
+            ftdi->eeprom->release_number = 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 OUT_IS_ISOCHRONOUS:
+            ftdi->eeprom->out_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 USE_USB_VERSION:
+            ftdi->eeprom->use_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 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;
+        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 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;
+        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 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;
+}
+
+/** 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;
+}
+
+/** 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
 
     \param ftdi pointer to ftdi_context
@@ -3010,20 +4107,20 @@ int ftdi_read_eeprom(struct ftdi_context *ftdi)
     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,
-                buf+(i*2), 2, ftdi->usb_read_timeout) != 2)
+                    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 
+    /*    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)
+    else if (memcmp(buf,&buf[0x80],0x80) == 0)
         ftdi->eeprom->size = 0x80;
-    else if(memcmp(buf,&buf[0x40],0x40) == 0)
+    else if (memcmp(buf,&buf[0x40],0x40) == 0)
         ftdi->eeprom->size = 0x40;
     else
         ftdi->eeprom->size = 0x100;
@@ -3095,7 +4192,7 @@ int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid)
     \retval -4: Device can't access unprotected area
     \retval -5: Reading chip type failed
 */
-int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, 
+int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr,
                                unsigned short eeprom_val)
 {
     int chip_type_location;
@@ -3104,35 +4201,38 @@ int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr,
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
 
-    if(eeprom_addr <0x80)
+    if (eeprom_addr <0x80)
         ftdi_error_return(-2, "Invalid access to checksum protected area  below 0x80");
 
 
     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");
+        case TYPE_BM:
+        case  TYPE_2232C:
+            chip_type_location = 0x14;
+            break;
+        case TYPE_2232H:
+        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");
-    fprintf(stderr," loc 0x%04x val 0x%04x\n", chip_type_location,chip_type); 
-    if((chip_type & 0xff) != 0x66)
+    if (ftdi_read_eeprom_location( ftdi, chip_type_location>>1, &chip_type))
+        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)
     {
         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;
@@ -3142,10 +4242,11 @@ int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr,
     Write eeprom
 
     \param ftdi pointer to ftdi_context
+
     \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)
 {
@@ -3155,6 +4256,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 */
@@ -3167,6 +4272,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,
@@ -3199,42 +4309,42 @@ 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;
     }
 
-    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_ERASE_EEPROM_REQUEST, 
+    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)
+                                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)
+    if (ftdi_read_eeprom_location( ftdi, 0x00, &eeprom_value))
+        ftdi_error_return(-4, "Reading failed");
+    if (eeprom_value == MAGIC)
     {
         ftdi->eeprom->chip = 0x46;
     }
-    else 
+    else
     {
-        if (ftdi_read_eeprom_location( ftdi, 0x40, &eeprom_value)) 
-            ftdi_error_return(-4, "Reading failed failed");
-        if(eeprom_value == MAGIC)
+        if (ftdi_read_eeprom_location( ftdi, 0x40, &eeprom_value))
+            ftdi_error_return(-4, "Reading failed");
+        if (eeprom_value == MAGIC)
             ftdi->eeprom->chip = 0x56;
-        else 
+        else
         {
-            if (ftdi_read_eeprom_location( ftdi, 0xc0, &eeprom_value)) 
-                ftdi_error_return(-4, "Reading failed failed");
-            if(eeprom_value == MAGIC)
+            if (ftdi_read_eeprom_location( ftdi, 0xc0, &eeprom_value))
+                ftdi_error_return(-4, "Reading failed");
+            if (eeprom_value == MAGIC)
                 ftdi->eeprom->chip = 0x66;
             else
             {
@@ -3242,7 +4352,7 @@ int ftdi_erase_eeprom(struct ftdi_context *ftdi)
             }
         }
     }
-    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_ERASE_EEPROM_REQUEST, 
+    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;