We are out of battery power (remove wrong comment)
[libftdi] / src / ftdi.c
index a461e19..5cf2012 100644 (file)
@@ -73,11 +73,13 @@ static void ftdi_usb_close_internal (struct ftdi_context *ftdi)
 
     \retval  0: all fine
     \retval -1: couldn't allocate read buffer
+    \retval -2: couldn't allocate struct  buffer
 
     \remark This should be called before all functions
 */
 int ftdi_init(struct ftdi_context *ftdi)
 {
+    struct ftdi_eeprom* eeprom = (struct ftdi_eeprom *)malloc(sizeof(struct ftdi_eeprom));
     ftdi->usb_ctx = NULL;
     ftdi->usb_dev = NULL;
     ftdi->usb_read_timeout = 5000;
@@ -101,7 +103,10 @@ int ftdi_init(struct ftdi_context *ftdi)
 
     ftdi->error_str = NULL;
 
-    ftdi->eeprom = NULL;
+    if (eeprom == 0)
+        ftdi_error_return(-2, "Can't malloc struct ftdi_eeprom");
+    memset(eeprom, 0, sizeof(struct ftdi_eeprom));
+    ftdi->eeprom = eeprom;
 
     /* All fine. Now allocate the readbuffer */
     return ftdi_read_data_set_chunksize(ftdi, 4096);
@@ -192,6 +197,27 @@ void ftdi_deinit(struct ftdi_context *ftdi)
         free(ftdi->readbuffer);
         ftdi->readbuffer = NULL;
     }
+
+    if (ftdi->eeprom != NULL)
+    {
+        if (ftdi->eeprom->manufacturer != 0)
+        {
+            free(ftdi->eeprom->manufacturer);
+            ftdi->eeprom->manufacturer = 0;
+        }
+        if (ftdi->eeprom->product != 0)
+        {
+            free(ftdi->eeprom->product);
+            ftdi->eeprom->product = 0;
+        }
+        if (ftdi->eeprom->serial != 0)
+        {
+            free(ftdi->eeprom->serial);
+            ftdi->eeprom->serial = 0;
+        }
+        free(ftdi->eeprom);
+        ftdi->eeprom = NULL;
+    }
     libusb_exit(ftdi->usb_ctx);
 }
 
@@ -2153,37 +2179,26 @@ int ftdi_set_error_char(struct ftdi_context *ftdi,
 }
 
 /**
-   Set the eeprom size
-
-   \param ftdi pointer to ftdi_context
-   \param eeprom Pointer to ftdi_eeprom
-   \param size
-
-*/
-void ftdi_eeprom_setsize(struct ftdi_context *ftdi, struct ftdi_eeprom *eeprom, int size)
-{
-    if (ftdi == NULL)
-        return;
-
-    ftdi->eeprom = eeprom;
-    ftdi->eeprom->size=size;
-}
-
-/**
     Init eeprom with default values.
+    \param ftdi pointer to ftdi_context
+    \param manufacturer String to use as Manufacturer
+    \param product String to use as Product description
+    \param serial String to use as Serial number description
 
-    \param eeprom Pointer to ftdi_eeprom
+    \retval  0: all fine
+    \retval -1: No struct ftdi_context
+    \retval -2: No struct ftdi_eeprom
 */
-void ftdi_eeprom_initdefaults(struct ftdi_context *ftdi)
+int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
+                              char * product, char * serial)
 {
-    int i;
     struct ftdi_eeprom *eeprom;
 
     if (ftdi == NULL)
-        return;
+        ftdi_error_return(-1, "No struct ftdi_context");
 
     if (ftdi->eeprom == NULL)
-        return;
+        ftdi_error_return(-2,"No struct ftdi_eeprom"); 
 
     eeprom = ftdi->eeprom;
     memset(eeprom, 0, sizeof(struct ftdi_eeprom));
@@ -2195,68 +2210,62 @@ void ftdi_eeprom_initdefaults(struct ftdi_context *ftdi)
         eeprom->product_id = 0x6001;
     else
         eeprom->product_id = 0x6010;
-    switch (ftdi->type)
-    {
-    case TYPE_2232C:
-        eeprom->release = 0x500;
-        break;
-    case TYPE_2232H:
-        eeprom->release = 0x200;
-        break;
-    default:
-        eeprom->release = 0;
-    }
     if (ftdi->type == TYPE_AM)
         eeprom->usb_version = 0x0101;
     else
         eeprom->usb_version = 0x0200;
-    eeprom->max_power = 50;
+    eeprom->max_power = 100;
 
+    if (eeprom->manufacturer)
+        free (eeprom->manufacturer);
     eeprom->manufacturer = NULL;
+    if (manufacturer)
+    {
+        eeprom->manufacturer = malloc(strlen(manufacturer)+1);
+        if (eeprom->manufacturer)
+            strcpy(eeprom->manufacturer, manufacturer);
+    }
+
+    if (eeprom->product)
+        free (eeprom->product);
     eeprom->product = NULL;
+    {
+        eeprom->product = malloc(strlen(product)+1);
+        if (eeprom->product)
+            strcpy(eeprom->product, product);
+    }
+
+    if (eeprom->serial)
+        free (eeprom->serial);
     eeprom->serial = NULL;
+    if (serial)
+    {
+        eeprom->serial = malloc(strlen(serial)+1);
+        if (eeprom->serial)
+            strcpy(eeprom->serial, serial);
+    }
+
 
     if(ftdi->type == TYPE_R)
+    {
+        eeprom->max_power = 90;
         eeprom->size = 0x80;
+        eeprom->cbus_function[0] = CBUS_TXLED;
+        eeprom->cbus_function[1] = CBUS_RXLED;
+        eeprom->cbus_function[2] = CBUS_TXDEN;
+        eeprom->cbus_function[3] = CBUS_PWREN;
+        eeprom->cbus_function[4] = CBUS_SLEEP;
+    }
     else
         eeprom->size = -1;
+    return 0;
 }
 
 /**
-    Frees allocated memory in eeprom.
-
-    \param eeprom Pointer to ftdi_eeprom
-*/
-void ftdi_eeprom_free(struct ftdi_context *ftdi)
-{
-    if (!ftdi)
-        return;
-    if (ftdi->eeprom)
-    {
-        struct ftdi_eeprom *eeprom = ftdi->eeprom;
-
-        if (eeprom->manufacturer != 0) {
-            free(eeprom->manufacturer);
-            eeprom->manufacturer = 0;
-        }
-        if (eeprom->product != 0) {
-            free(eeprom->product);
-            eeprom->product = 0;
-        }
-        if (eeprom->serial != 0) {
-            free(eeprom->serial);
-            eeprom->serial = 0;
-        }
-    }
-}
-
-/**
-    Build binary output from ftdi_eeprom structure.
+    Build binary buffer from ftdi_eeprom structure.
     Output is suitable for ftdi_write_eeprom().
 
-    \note This function doesn't handle FT2232x devices. Only FT232x.
-    \param eeprom Pointer to ftdi_eeprom
-    \param output Buffer of 128 bytes to store eeprom image to
+    \param ftdi pointer to ftdi_context
 
     \retval >0: free eeprom size
     \retval -1: eeprom size (128 bytes) exceeded by custom strings
@@ -2266,13 +2275,14 @@ void ftdi_eeprom_free(struct ftdi_context *ftdi)
     \retval -5: Chip doesn't support high current drive
     \retval -6: No connected EEPROM or EEPROM Type unknown
 */
-int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
+int ftdi_eeprom_build(struct ftdi_context *ftdi)
 {
-    unsigned char i, j;
+    unsigned char i, j, eeprom_size_mask;
     unsigned short checksum, value;
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
     int size_check;
     struct ftdi_eeprom *eeprom;
+    unsigned char * output;
 
     if (ftdi == NULL)
         ftdi_error_return(-2,"No context");
@@ -2280,9 +2290,15 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
         ftdi_error_return(-2,"No eeprom structure");
 
     eeprom= ftdi->eeprom;
+    output = eeprom->buf;
 
     if(eeprom->chip == -1)
-        ftdi_error_return(-5,"No connected EEPROM or EEPROM Type unknown");
+        ftdi_error_return(-5,"No connected EEPROM or EEPROM type unknown");
+
+    if ((eeprom->chip == 0x56) || (eeprom->chip == 0x66))
+        eeprom->size = 0x100;
+    else
+        eeprom->size = 0x80;
 
     if (eeprom->manufacturer != NULL)
         manufacturer_size = strlen(eeprom->manufacturer);
@@ -2311,21 +2327,17 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
     size_check -= serial_size*2;
 
     /* Space for the string type and pointer bytes */
-    size_check -= -6;
+    size_check -= -9;
 
     // eeprom size exceeded?
     if (size_check < 0)
         return (-1);
 
     // empty eeprom
-    memset (output, 0, eeprom->size);
+    memset (ftdi->eeprom->buf, 0, FTDI_MAX_EEPROM_SIZE);
+
+    // Bytes and Bits set for all Types
 
-    // Addr 00: High current IO
-    output[0x00] = eeprom->high_current_a ? HIGH_CURRENT_DRIVE : 0;
-    // Addr 01: IN endpoint size (for R type devices, different for FT2232)
-    if (ftdi->type == TYPE_R) {
-        output[0x01] = 0x40;
-    }
     // Addr 02: Vendor ID
     output[0x02] = eeprom->vendor_id;
     output[0x03] = eeprom->vendor_id >> 8;
@@ -2336,7 +2348,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
 
     // Addr 06: Device release number (0400h for BM features)
     output[0x06] = 0x00;
-    switch (eeprom->release) {
+    switch (ftdi->type) {
         case TYPE_AM:
             output[0x07] = 0x02;
             break;
@@ -2349,6 +2361,12 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
         case TYPE_R:
             output[0x07] = 0x06;
             break;
+         case TYPE_2232H:
+            output[0x07] = 0x07;
+            break;
+         case TYPE_4232H:
+            output[0x07] = 0x08;
+            break;
         default:
             output[0x07] = 0x00;
     }
@@ -2357,7 +2375,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
     // Bit 7: always 1
     // Bit 6: 1 if this device is self powered, 0 if bus powered
     // Bit 5: 1 if this device uses remote wakeup
-    // Bit 4: 1 if this device is battery powered
+    // Bit 4-0: reserved - 0
     j = 0x80;
     if (eeprom->self_powered == 1)
         j |= 0x40;
@@ -2366,98 +2384,254 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
     output[0x08] = j;
 
     // Addr 09: Max power consumption: max power = value * 2 mA
-    output[0x09] = eeprom->max_power;
-
-    // Addr 0A: Chip configuration
-    // Bit 7: 0 - reserved
-    // Bit 6: 0 - reserved
-    // Bit 5: 0 - reserved
-    // Bit 4: 1 - Change USB version 
-    //            not seen on FT2232C)
-    // Bit 3: 1 - Use the serial number string
-    // Bit 2: 1 - Enable suspend pull downs for lower power
-    // Bit 1: 1 - Out EndPoint is Isochronous
-    // Bit 0: 1 - In EndPoint is Isochronous
-    //
-    j = 0;
-    if (eeprom->in_is_isochronous == 1)
-        j = j | 1;
-    if (eeprom->out_is_isochronous == 1)
-        j = j | 2;
-    if (eeprom->suspend_pull_downs == 1)
-        j = j | 4;
-    if (eeprom->use_serial == 1)
-        j = j | 8;
-    output[0x0A] = j;
-
-    // Addr 0B: Invert data lines
-    output[0x0B] = eeprom->invert & 0xff;
-
-    // Addr 0C: USB version low byte
-    // Addr 0D: USB version high byte
-    output[0x0C] = eeprom->usb_version;
-    output[0x0D] = eeprom->usb_version >> 8;
+    output[0x09] = eeprom->max_power>>1;
+
+    if(ftdi->type != TYPE_AM)
+    {
+        // Addr 0A: Chip configuration
+        // Bit 7: 0 - reserved
+        // Bit 6: 0 - reserved
+        // Bit 5: 0 - reserved
+        // Bit 4: 1 - Change USB version 
+        // Bit 3: 1 - Use the serial number string
+        // Bit 2: 1 - Enable suspend pull downs for lower power
+        // Bit 1: 1 - Out EndPoint is Isochronous
+        // Bit 0: 1 - In EndPoint is Isochronous
+        //
+        j = 0;
+        if (eeprom->in_is_isochronous == 1)
+            j = j | 1;
+        if (eeprom->out_is_isochronous == 1)
+            j = j | 2;
+        output[0x0A] = j;
+    }
 
+    // Dynamic content
+    // Strings start at 0x94 (TYPE_AM, TYPE_BM)
+    // 0x96 (TYPE_2232C), 0x98 (TYPE_R) and 0x9a (TYPE_x232H)
+    i = 0;
+    switch(ftdi->type)
+    {
+    case TYPE_2232H:
+    case TYPE_4232H:
+        i += 2;
+    case TYPE_R:
+        i += 2;
+    case TYPE_2232C:
+        i += 2;
+    case TYPE_AM:
+    case TYPE_BM:
+        i += 0x94;
+    }
+    /* Wrap around 0x80 for 128 byte EEPROMS (Internale and 93x46) */
+    eeprom_size_mask = eeprom->size -1;
 
     // Addr 0E: Offset of the manufacturer string + 0x80, calculated later
     // Addr 0F: Length of manufacturer string
+    // Output manufacturer
+    output[0x0E] = i;  // calculate offset
+    output[i & eeprom_size_mask] = manufacturer_size*2 + 2, i++;
+    output[i & eeprom_size_mask] = 0x03, i++; // type: string
+    for (j = 0; j < manufacturer_size; j++)
+    {
+        output[i & eeprom_size_mask] = eeprom->manufacturer[j], i++;
+        output[i & eeprom_size_mask] = 0x00, i++;
+    }
     output[0x0F] = manufacturer_size*2 + 2;
 
     // Addr 10: Offset of the product string + 0x80, calculated later
     // Addr 11: Length of product string
+    output[0x10] = i | 0x80;  // calculate offset
+    output[i & eeprom_size_mask] = product_size*2 + 2, i++;
+    output[i & eeprom_size_mask] = 0x03, i++;
+    for (j = 0; j < product_size; j++)
+    {
+        output[i & eeprom_size_mask] = eeprom->product[j], i++;
+        output[i & eeprom_size_mask] = 0x00, i++;
+    }
     output[0x11] = product_size*2 + 2;
 
     // Addr 12: Offset of the serial string + 0x80, calculated later
     // Addr 13: Length of serial string
-    output[0x13] = serial_size*2 + 2;
-
-    // Addr 14: CBUS function: CBUS0, CBUS1
-    // Addr 15: CBUS function: CBUS2, CBUS3
-    // Addr 16: CBUS function: CBUS5
-    output[0x14] = eeprom->cbus_function[0] | (eeprom->cbus_function[1] << 4);
-    output[0x15] = eeprom->cbus_function[2] | (eeprom->cbus_function[3] << 4);
-    output[0x16] = eeprom->cbus_function[4];
-    // Addr 17: Unknown
-
-    // Dynamic content
-    // In images produced by FTDI's FT_Prog for FT232R strings start at 0x18
-    // Space till 0x18 should be considered as reserved.
-    if (ftdi->type >= TYPE_R) {
-        i = 0x18;
-    } else {
-        i = 0x14;
+    output[0x12] = i | 0x80; // calculate offset
+    output[i & eeprom_size_mask] = serial_size*2 + 2, i++;
+    output[i & eeprom_size_mask] = 0x03, i++;
+    for (j = 0; j < serial_size; j++)
+    {
+        output[i & eeprom_size_mask] = eeprom->serial[j], i++;
+        output[i & eeprom_size_mask] = 0x00, i++;
     }
-    if (eeprom->size >= 256) i = 0x80;
+    output[i & eeprom_size_mask] = 0x02; /* as seen when written with FTD2XX */
+    i++;
+    output[i & eeprom_size_mask] = 0x03; /* as seen when written with FTD2XX */
+    i++;
+    output[i & eeprom_size_mask] = eeprom->is_not_pnp; /* as seen when written with FTD2XX */
+    i++;
 
+    output[0x13] = serial_size*2 + 2;
 
-    // Output manufacturer
-    output[0x0E] = i | 0x80;  // calculate offset
-    output[i++] = manufacturer_size*2 + 2;
-    output[i++] = 0x03; // type: string
-    for (j = 0; j < manufacturer_size; j++)
+    if(ftdi->type > TYPE_AM) /*use_serial not used in AM devices*/
     {
-        output[i] = eeprom->manufacturer[j], i++;
-        output[i] = 0x00, i++;
+        if (eeprom->use_serial == USE_SERIAL_NUM )
+            output[0x0A] |= USE_SERIAL_NUM;
+        else
+            output[0x0A] &= ~USE_SERIAL_NUM;
     }
+    /* Fixme: ftd2xx seems to append 0x02, 0x03 and 0x01 for PnP = 0 or 0x00 else */
+    // calculate checksum
 
-    // Output product name
-    output[0x10] = i | 0x80;  // calculate offset
-    output[i] = product_size*2 + 2, i++;
-    output[i] = 0x03, i++;
-    for (j = 0; j < product_size; j++)
+    /* Bytes and Bits specific to (some) types
+       Write linear, as this allows easier fixing*/
+    switch(ftdi->type)
     {
-        output[i] = eeprom->product[j], i++;
-        output[i] = 0x00, i++;
-    }
+    case TYPE_AM:
+        break;
+    case TYPE_BM:
+        output[0x0C] = eeprom->usb_version & 0xff;
+        output[0x0D] = (eeprom->usb_version>>8) & 0xff;
+        output[0x14] = eeprom->chip;
+        break;
+    case TYPE_2232C:
 
-    // Output serial
-    output[0x12] = i | 0x80; // calculate offset
-    output[i] = serial_size*2 + 2, i++;
-    output[i] = 0x03, i++;
-    for (j = 0; j < serial_size; j++)
-    {
-        output[i] = eeprom->serial[j], i++;
-        output[i] = 0x00, i++;
+        output[0x00] = (eeprom->channel_a_type);
+        if ( eeprom->channel_a_driver == DRIVER_VCP)
+            output[0x00] |= DRIVER_VCP;
+        else
+            output[0x00] &= ~DRIVER_VCP;
+
+        if ( eeprom->high_current_a == HIGH_CURRENT_DRIVE)
+            output[0x00] |= HIGH_CURRENT_DRIVE;
+        else
+            output[0x00] &= ~HIGH_CURRENT_DRIVE;
+
+        output[0x01] = (eeprom->channel_b_type);
+        if ( eeprom->channel_b_driver == DRIVER_VCP)
+            output[0x01] |= DRIVER_VCP;
+        else
+            output[0x01] &= ~DRIVER_VCP;
+
+        if ( eeprom->high_current_b == HIGH_CURRENT_DRIVE)
+            output[0x01] |= HIGH_CURRENT_DRIVE;
+        else
+            output[0x01] &= ~HIGH_CURRENT_DRIVE;
+
+        if (eeprom->in_is_isochronous == 1)
+            output[0x0A] |= 0x1;
+        else
+            output[0x0A] &= ~0x1;
+        if (eeprom->out_is_isochronous == 1)
+            output[0x0A] |= 0x2;
+        else
+            output[0x0A] &= ~0x2;
+        if (eeprom->suspend_pull_downs == 1)
+            output[0x0A] |= 0x4;
+        else
+            output[0x0A] &= ~0x4;
+        output[0x0C] = eeprom->usb_version & 0xff;
+        output[0x0D] = (eeprom->usb_version>>8) & 0xff;
+        output[0x14] = eeprom->chip;
+        break;
+    case TYPE_R:
+        if(eeprom->high_current == HIGH_CURRENT_DRIVE_R)
+            output[0x00] |= HIGH_CURRENT_DRIVE_R;
+        output[0x01] = 0x40; /* Hard coded Endpoint Size*/
+
+        if (eeprom->suspend_pull_downs == 1)
+            output[0x0A] |= 0x4;
+        else
+            output[0x0A] &= ~0x4;
+        output[0x0B] = eeprom->invert;
+        output[0x0C] = eeprom->usb_version & 0xff;
+        output[0x0D] = (eeprom->usb_version>>8) & 0xff;
+
+        if(eeprom->cbus_function[0] > CBUS_BB)
+            output[0x14] = CBUS_TXLED;
+        else
+            output[0x14] = eeprom->cbus_function[0];
+
+        if(eeprom->cbus_function[1] > CBUS_BB)
+            output[0x14] |= CBUS_RXLED<<4;
+        else
+            output[0x14] |= eeprom->cbus_function[1]<<4;
+
+        if(eeprom->cbus_function[2] > CBUS_BB)
+            output[0x15] = CBUS_TXDEN;
+        else
+            output[0x15] = eeprom->cbus_function[2];
+
+        if(eeprom->cbus_function[3] > CBUS_BB)
+            output[0x15] |= CBUS_PWREN<<4;
+        else
+            output[0x15] |= eeprom->cbus_function[3]<<4;
+
+        if(eeprom->cbus_function[4] > CBUS_CLK6)
+            output[0x16] = CBUS_SLEEP;
+        else
+            output[0x16] = eeprom->cbus_function[4];
+        break;
+    case TYPE_2232H:
+        output[0x00] = (eeprom->channel_a_type);
+        if ( eeprom->channel_a_driver == DRIVER_VCP)
+            output[0x00] |= DRIVER_VCP;
+        else
+            output[0x00] &= ~DRIVER_VCP;
+
+        output[0x01] = (eeprom->channel_b_type);
+        if ( eeprom->channel_b_driver == DRIVER_VCP)
+            output[0x01] |= DRIVER_VCP;
+        else
+            output[0x01] &= ~DRIVER_VCP;
+        if(eeprom->suspend_dbus7 == SUSPEND_DBUS7)
+            output[0x01] |= SUSPEND_DBUS7;
+        else
+            output[0x01] &= ~SUSPEND_DBUS7;
+
+        if (eeprom->suspend_pull_downs == 1)
+            output[0x0A] |= 0x4;
+        else
+            output[0x0A] &= ~0x4;
+
+        if(eeprom->group0_drive > DRIVE_16MA)
+            output[0x0c] |= DRIVE_16MA;
+        else
+            output[0x0c] |= eeprom->group0_drive;
+        if (eeprom->group0_schmitt == IS_SCHMITT)
+            output[0x0c] |= IS_SCHMITT;
+        if (eeprom->group0_slew == SLOW_SLEW)
+            output[0x0c] |= SLOW_SLEW;
+
+        if(eeprom->group1_drive > DRIVE_16MA)
+            output[0x0c] |= DRIVE_16MA<<4;
+        else
+            output[0x0c] |= eeprom->group1_drive<<4;
+        if (eeprom->group1_schmitt == IS_SCHMITT)
+            output[0x0c] |= IS_SCHMITT<<4;
+        if (eeprom->group1_slew == SLOW_SLEW)
+            output[0x0c] |= SLOW_SLEW<<4;
+
+        if(eeprom->group2_drive > DRIVE_16MA)
+            output[0x0d] |= DRIVE_16MA;
+        else
+            output[0x0d] |= eeprom->group2_drive;
+        if (eeprom->group2_schmitt == IS_SCHMITT)
+            output[0x0d] |= IS_SCHMITT;
+        if (eeprom->group2_slew == SLOW_SLEW)
+            output[0x0d] |= SLOW_SLEW;
+
+        if(eeprom->group3_drive > DRIVE_16MA)
+            output[0x0d] |= DRIVE_16MA<<4;
+        else
+            output[0x0d] |= eeprom->group3_drive<<4;
+        if (eeprom->group3_schmitt == IS_SCHMITT)
+            output[0x0d] |= IS_SCHMITT<<4;
+        if (eeprom->group3_slew == SLOW_SLEW)
+            output[0x0d] |= SLOW_SLEW<<4;
+
+        output[0x18] = eeprom->chip;
+
+        break;
+    case TYPE_4232H:
+        fprintf(stderr,"FIXME: Build FT4232H specific EEPROM settings\n");
     }
 
     // calculate checksum
@@ -2481,53 +2655,32 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
 /**
    Decode binary EEPROM image into an ftdi_eeprom structure.
 
-   \param eeprom Pointer to ftdi_eeprom which will be filled in.
-   \param buf Buffer of \a size bytes of raw eeprom data
-   \param size size size of eeprom data in bytes
-
+   \param ftdi pointer to ftdi_context
+   \param verbose Decode EEPROM on stdout
+   
    \retval 0: all fine
    \retval -1: something went wrong
 
    FIXME: How to pass size? How to handle size field in ftdi_eeprom?
    FIXME: Strings are malloc'ed here and should be freed somewhere
 */
-int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size, int verbose)
+int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 {
     unsigned char i, j;
     unsigned short checksum, eeprom_checksum, value;
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
     int eeprom_size;
     struct ftdi_eeprom *eeprom;
+    unsigned char *buf = ftdi->eeprom->buf;
+    int release;
 
     if (ftdi == NULL)
         ftdi_error_return(-1,"No context");
     if (ftdi->eeprom == NULL)
         ftdi_error_return(-1,"No eeprom structure");
  
-    eeprom_size = ftdi->eeprom->size;
-    if(ftdi->type == TYPE_R)
-        eeprom_size = 0x80;
     eeprom = ftdi->eeprom;
-
-    // Addr 00: Channel A setting
-
-    eeprom->channel_a_type   = buf[0x00] & 0x7;
-    eeprom->high_current     = buf[0x00] & HIGH_CURRENT_DRIVE_R;
-    eeprom->channel_a_driver = buf[0x00] & DRIVER_VCP;
-    eeprom->high_current_a   = buf[0x00] & HIGH_CURRENT_DRIVE;
-
-    // Addr 01: Channel B setting
-
-    eeprom->channel_b_type   = buf[0x01] & 0x7;
-    eeprom->channel_b_driver = buf[0x01] & DRIVER_VCP;
-    eeprom->high_current_b   = buf[0x01] & HIGH_CURRENT_DRIVE;
-
-    eeprom->suspend_dbus7    = buf[0x01] & SUSPEND_DBUS7;
-
-    if((ftdi->type == TYPE_R) && ((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_size = eeprom->size;
 
     // Addr 02: Vendor ID
     eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8);
@@ -2535,13 +2688,12 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
     // Addr 04: Product ID
     eeprom->product_id = buf[0x04] + (buf[0x05] << 8);
 
-    eeprom->release = buf[0x06] + (buf[0x07]<<8);
+    release = buf[0x06] + (buf[0x07]<<8);
 
     // Addr 08: Config descriptor
     // Bit 7: always 1
     // Bit 6: 1 if this device is self powered, 0 if bus powered
     // Bit 5: 1 if this device uses remote wakeup
-    // Bit 4: 1 if this device is battery powered
     eeprom->self_powered = buf[0x08] & 0x40;
     eeprom->remote_wakeup = buf[0x08] & 0x20;;
 
@@ -2576,6 +2728,8 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
     // Addr 0E: Offset of the manufacturer string + 0x80, calculated later
     // Addr 0F: Length of manufacturer string
     manufacturer_size = buf[0x0F]/2;
+    if(eeprom->manufacturer)
+        free(eeprom->manufacturer);
     if (manufacturer_size > 0) 
     {
         eeprom->manufacturer = malloc(manufacturer_size);
@@ -2594,6 +2748,8 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
 
     // Addr 10: Offset of the product string + 0x80, calculated later
     // Addr 11: Length of product string
+    if(eeprom->product)
+        free(eeprom->product);
     product_size = buf[0x11]/2;
     if (product_size > 0)
     {
@@ -2613,6 +2769,8 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
 
     // Addr 12: Offset of the serial string + 0x80, calculated later
     // Addr 13: Length of serial string
+    if(eeprom->serial)
+        free(eeprom->serial);
     serial_size = buf[0x13]/2;
     if (serial_size > 0)
     {
@@ -2650,16 +2808,33 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
         ftdi_error_return(-1,"EEPROM checksum error");
     }
 
-    else if ((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM))
+    eeprom->channel_a_type   = 0;
+    if ((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM))
     {
         eeprom->chip = -1;
     }
     else if(ftdi->type == TYPE_2232C)
     {
+        eeprom->channel_a_type   = buf[0x00] & 0x7;
+        eeprom->channel_a_driver = buf[0x00] & DRIVER_VCP;
+        eeprom->high_current_a   = buf[0x00] & HIGH_CURRENT_DRIVE;
+        eeprom->channel_b_type   = buf[0x01] & 0x7;
+        eeprom->channel_b_driver = buf[0x01] & DRIVER_VCP;
+        eeprom->high_current_b   = buf[0x01] & HIGH_CURRENT_DRIVE;
         eeprom->chip = buf[0x14];
     }
     else if(ftdi->type == TYPE_R)
     {
+        /* TYPE_R flags D2XX, not VCP as all others*/
+        eeprom->channel_a_driver = (~buf[0x00]) & DRIVER_VCP;
+        eeprom->high_current     = buf[0x00] & HIGH_CURRENT_DRIVE_R;
+        if( (buf[0x01]&0x40) != 0x40)
+        fprintf(stderr,
+                "TYPE_R EEPROM byte[0x01] Bit 6 unexpected Endpoint size."
+                " If this happened with the\n"
+                " EEPROM programmed by FTDI tools, please report "
+                "to libftdi@developer.intra2net.com\n");
+
         eeprom->chip = buf[0x16];
         // Addr 0B: Invert data lines
         // Works only on FT232R, not FT245R, but no way to distinguish
@@ -2675,6 +2850,14 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
     }
     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;
+
         eeprom->chip = buf[0x18];
         eeprom->group0_drive   =  buf[0x0c]       & DRIVE_16MA;
         eeprom->group0_schmitt =  buf[0x0c]       & IS_SCHMITT;
@@ -2695,7 +2878,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
         char *channel_mode[] = {"UART","245","CPU", "unknown", "OPTO"};
         fprintf(stdout, "VID:     0x%04x\n",eeprom->vendor_id);
         fprintf(stdout, "PID:     0x%04x\n",eeprom->product_id);
-        fprintf(stdout, "Release: 0x%04x\n",eeprom->release);
+        fprintf(stdout, "Release: 0x%04x\n",release);
 
         if(eeprom->self_powered)
             fprintf(stdout, "Self-Powered%s", (eeprom->remote_wakeup)?", USB Remote Wake Up\n":"\n");
@@ -2719,16 +2902,17 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
             fprintf(stdout, "Pull IO pins low during suspend\n");
         if(eeprom->remote_wakeup)
             fprintf(stdout, "Enable Remote Wake Up\n");
+        fprintf(stdout, "PNP: %d\n",(eeprom->is_not_pnp)?0:1);
         if (ftdi->type >= TYPE_2232C)
             fprintf(stdout,"Channel A has Mode %s%s%s\n", 
                     channel_mode[eeprom->channel_a_type],
                     (eeprom->channel_a_driver)?" VCP":"",
-                    (eeprom->high_current_a)?" High Currenr IO":"");
-        if (ftdi->type >= TYPE_2232C)
+                    (eeprom->high_current_a)?" High Current IO":"");
+        if ((ftdi->type >= TYPE_2232C) && (ftdi->type != TYPE_R)) 
             fprintf(stdout,"Channel B has Mode %s%s%s\n", 
                     channel_mode[eeprom->channel_b_type],
                     (eeprom->channel_b_driver)?" VCP":"",
-                    (eeprom->high_current_b)?" High Currenr IO":"");
+                    (eeprom->high_current_b)?" High Current IO":"");
         if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H)) 
         {
             fprintf(stdout,"%s has %d mA drive%s%s\n",
@@ -2752,9 +2936,34 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size,
                     (eeprom->group3_schmitt)?" Schmitt Input":"",
                     (eeprom->group3_slew)?" Slow Slew":"");
         }
-
+        if (ftdi->type == TYPE_R)
+        {
+            char *cbus_mux[] = {"TXDEN","PWREN","RXLED", "TXLED","TX+RXLED",
+                                "SLEEP","CLK48","CLK24","CLK12","CLK6",
+                                "IOMODE","BB_WR","BB_RD"};
+            char *cbus_BB[] = {"RXF","TXE","RD", "WR"};
+            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->cbus_function[i]<CBUS_BB)
+                    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]);
+            }
+        }
     }
-
     return 0;
 }
 
@@ -2784,34 +2993,37 @@ int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsig
     Read eeprom
 
     \param ftdi pointer to ftdi_context
-    \param eeprom Pointer to store eeprom into
 
     \retval  0: all fine
     \retval -1: read failed
     \retval -2: USB device unavailable
 */
-int ftdi_read_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom)
+int ftdi_read_eeprom(struct ftdi_context *ftdi)
 {
     int i;
+    unsigned char *buf;
 
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
+    buf = ftdi->eeprom->buf;
 
     for (i = 0; i < FTDI_MAX_EEPROM_SIZE/2; i++)
     {
-        if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_EEPROM_REQUEST, 0, i, eeprom+(i*2), 2, ftdi->usb_read_timeout) != 2)
+        if (libusb_control_transfer(
+                ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE,SIO_READ_EEPROM_REQUEST, 0, i,
+                buf+(i*2), 2, ftdi->usb_read_timeout) != 2)
             ftdi_error_return(-1, "reading eeprom failed");
     }
 
     if (ftdi->type == TYPE_R)
-        ftdi->eeprom->size = 0xa0;
+        ftdi->eeprom->size = 0x80;
     /*    Guesses size of eeprom by comparing halves 
           - will not work with blank eeprom */
-    else if (strrchr((const char *)eeprom, 0xff) == ((const char *)eeprom +FTDI_MAX_EEPROM_SIZE -1))
+    else if (strrchr((const char *)buf, 0xff) == ((const char *)buf +FTDI_MAX_EEPROM_SIZE -1))
         ftdi->eeprom->size = -1;
-    else if(memcmp(eeprom,&eeprom[0x80],0x80) == 0)
+    else if(memcmp(buf,&buf[0x80],0x80) == 0)
         ftdi->eeprom->size = 0x80;
-    else if(memcmp(eeprom,&eeprom[0x40],0x40) == 0)
+    else if(memcmp(buf,&buf[0x40],0x40) == 0)
         ftdi->eeprom->size = 0x40;
     else
         ftdi->eeprom->size = 0x100;
@@ -2877,14 +3089,47 @@ int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid)
     \param eeprom_val Value to be written
 
     \retval  0: all fine
-    \retval -1: read failed
+    \retval -1: write failed
     \retval -2: USB device unavailable
+    \retval -3: Invalid access to checksum protected area below 0x80
+    \retval -4: Device can't access unprotected area
+    \retval -5: Reading chip type failed
 */
-int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, unsigned short eeprom_val)
+int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, 
+                               unsigned short eeprom_val)
 {
+    int chip_type_location;
+    unsigned short chip_type;
+
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
 
+    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");
+    }
+
+    if (ftdi_read_eeprom_location( ftdi, chip_type_location>>1, &chip_type)) 
+        ftdi_error_return(-5, "Reading failed failed");
+    fprintf(stderr," loc 0x%04x val 0x%04x\n", chip_type_location,chip_type); 
+    if((chip_type & 0xff) != 0x66)
+    {
+        ftdi_error_return(-6, "EEPROM is not of 93x66");
+    }
+
     if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
                                     SIO_WRITE_EEPROM_REQUEST, eeprom_val, eeprom_addr,
                                     NULL, 0, ftdi->usb_write_timeout) != 0)
@@ -2897,19 +3142,20 @@ int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, unsig
     Write eeprom
 
     \param ftdi pointer to ftdi_context
-    \param eeprom Pointer to read eeprom from
-
     \retval  0: all fine
     \retval -1: read failed
     \retval -2: USB device unavailable
 */
-int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom)
+int ftdi_write_eeprom(struct ftdi_context *ftdi)
 {
     unsigned short usb_val, status;
     int i, ret;
+    unsigned char *eeprom;
 
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
+    eeprom = ftdi->eeprom->buf;
 
     /* These commands were traced while running MProg */
     if ((ret = ftdi_usb_reset(ftdi)) != 0)
@@ -2968,7 +3214,9 @@ int ftdi_erase_eeprom(struct ftdi_context *ftdi)
        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( ftdi_write_eeprom_location(ftdi, 0xc0, MAGIC))
+    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
+                                    SIO_WRITE_EEPROM_REQUEST, MAGIC, 0xc0,
+                                    NULL, 0, ftdi->usb_write_timeout) != 0)
         ftdi_error_return(-3, "Writing magic failed");
     if (ftdi_read_eeprom_location( ftdi, 0x00, &eeprom_value)) 
         ftdi_error_return(-4, "Reading failed failed");