Support for FT232R eeprom features
[libftdi] / src / ftdi.c
index b42ba14..525f9d1 100644 (file)
@@ -2176,6 +2176,8 @@ void ftdi_eeprom_setsize(struct ftdi_context *ftdi, struct ftdi_eeprom *eeprom,
 */
 void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom)
 {
+    int i;
+
     if (eeprom == NULL)
         return;
 
@@ -2184,7 +2186,7 @@ void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom)
 
     eeprom->self_powered = 1;
     eeprom->remote_wakeup = 1;
-    eeprom->BM_type_chip = 1;
+    eeprom->chip_type = TYPE_BM;
 
     eeprom->in_is_isochronous = 0;
     eeprom->out_is_isochronous = 0;
@@ -2198,6 +2200,12 @@ void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom)
     eeprom->manufacturer = NULL;
     eeprom->product = NULL;
     eeprom->serial = NULL;
+    for (i=0; i < 5; i++)
+    {
+        eeprom->cbus_function[i] = 0;
+    }
+    eeprom->high_current = 0;
+    eeprom->invert = 0;
 
     eeprom->size = FTDI_DEFAULT_EEPROM_SIZE;
 }
@@ -2227,12 +2235,16 @@ void ftdi_eeprom_free(struct ftdi_eeprom *eeprom)
     Build binary output 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
 
-    \retval >0: used eeprom size
+    \retval >0: free eeprom size
     \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
 */
 int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
 {
@@ -2240,6 +2252,7 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
     unsigned short checksum, value;
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
     int size_check;
+    const int cbus_max[5] = {13, 13, 13, 13, 9};
 
     if (eeprom == NULL)
         return -2;
@@ -2251,6 +2264,18 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
     if (eeprom->serial != NULL)
         serial_size = strlen(eeprom->serial);
 
+    // highest allowed cbus value
+    for (i = 0; i < 5; i++)
+    {
+        if ((eeprom->cbus_function[i] > cbus_max[i]) ||
+            (eeprom->cbus_function[i] && eeprom->chip_type != TYPE_R)) return -3;
+    }
+    if (eeprom->chip_type != TYPE_R)
+    {
+        if (eeprom->invert) return -4;
+        if (eeprom->high_current) return -5;
+    }
+
     size_check = eeprom->size;
     size_check -= 28; // 28 are always in use (fixed)
 
@@ -2258,7 +2283,7 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
     // it seems that the FTDI chip will not read these strings from the lower half
     // Each string starts with two bytes; offset and type (0x03 for string)
     // the checksum needs two bytes, so without the string data that 8 bytes from the top half
-    if (eeprom->size>=256)size_check = 120;
+    if (eeprom->size>=256) size_check = 120;
     size_check -= manufacturer_size*2;
     size_check -= product_size*2;
     size_check -= serial_size*2;
@@ -2270,7 +2295,12 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
     // empty eeprom
     memset (output, 0, eeprom->size);
 
-    // Addr 00: Stay 00 00
+    // Addr 00: High current IO
+    output[0x00] = eeprom->high_current ? HIGH_CURRENT_DRIVE : 0;
+    // Addr 01: IN endpoint size (for R type devices, different for FT2232)
+    if (eeprom->chip_type == TYPE_R) {
+        output[0x01] = 0x40;
+    }
     // Addr 02: Vendor ID
     output[0x02] = eeprom->vendor_id;
     output[0x03] = eeprom->vendor_id >> 8;
@@ -2281,11 +2311,22 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
 
     // Addr 06: Device release number (0400h for BM features)
     output[0x06] = 0x00;
-
-    if (eeprom->BM_type_chip == 1)
-        output[0x07] = 0x04;
-    else
-        output[0x07] = 0x02;
+    switch (eeprom->chip_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;
+        default:
+            output[0x07] = 0x00;
+    }
 
     // Addr 08: Config descriptor
     // Bit 7: always 1
@@ -2325,8 +2366,8 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
         j = j | 16;
     output[0x0A] = j;
 
-    // Addr 0B: reserved
-    output[0x0B] = 0x00;
+    // Addr 0B: Invert data lines
+    output[0x0B] = eeprom->invert & 0xff;
 
     // Addr 0C: USB version low byte when 0x0A bit 4 is set
     // Addr 0D: USB version high byte when 0x0A bit 4 is set
@@ -2349,9 +2390,23 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
     // 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
-    i=0x14;
-    if (eeprom->size>=256) i = 0x80;
+    // In images produced by FTDI's FT_Prog for FT232R strings start at 0x18
+    // Space till 0x18 should be considered as reserved.
+    if (eeprom->chip_type >= TYPE_R) {
+        i = 0x18;
+    } else {
+        i = 0x14;
+    }
+    if (eeprom->size >= 256) i = 0x80;
 
 
     // Output manufacturer
@@ -2445,7 +2500,8 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
     // empty eeprom struct
     memset(eeprom, 0, sizeof(struct ftdi_eeprom));
 
-    // Addr 00: Stay 00 00
+    // Addr 00: High current IO
+    eeprom->high_current = (buf[0x02] & HIGH_CURRENT_DRIVE);
 
     // Addr 02: Vendor ID
     eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8);
@@ -2456,14 +2512,17 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
     value = buf[0x06] + (buf[0x07]<<8);
     switch (value)
     {
+        case 0x0600:
+            eeprom->chip_type = TYPE_R;
+            break;
         case 0x0400:
-            eeprom->BM_type_chip = 1;
+            eeprom->chip_type = TYPE_BM;
             break;
         case 0x0200:
-            eeprom->BM_type_chip = 0;
+            eeprom->chip_type = TYPE_AM;
             break;
         default: // Unknown device
-            eeprom->BM_type_chip = 0;
+            eeprom->chip_type = 0;
             break;
     }
 
@@ -2496,7 +2555,8 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
     if (j&0x08) eeprom->use_serial = 1;
     if (j&0x10) eeprom->change_usb_version = 1;
 
-    // Addr 0B: reserved
+    // Addr 0B: Invert data lines
+    eeprom->invert = buf[0x0B];
 
     // Addr 0C: USB version low byte when 0x0A bit 4 is set
     // Addr 0D: USB version high byte when 0x0A bit 4 is set
@@ -2523,6 +2583,19 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
     if (serial_size > 0) eeprom->serial = malloc(serial_size);
     else eeprom->serial = NULL;
 
+    // Addr 14: CBUS function: CBUS0, CBUS1
+    // Addr 15: CBUS function: CBUS2, CBUS3
+    // Addr 16: CBUS function: CBUS5
+    if (eeprom->chip_type == TYPE_R) {
+        eeprom->cbus_function[0] = buf[0x14] & 0x0f;
+        eeprom->cbus_function[1] = (buf[0x14] >> 4) & 0x0f;
+        eeprom->cbus_function[2] = buf[0x15] & 0x0f;
+        eeprom->cbus_function[3] = (buf[0x15] >> 4) & 0x0f;
+        eeprom->cbus_function[4] = buf[0x16] & 0x0f;
+    } else {
+        for (j=0; j<5; j++) eeprom->cbus_function[j] = 0;
+    }
+
     // Decode manufacturer
     i = buf[0x0E] & 0x7f; // offset
     for (j=0;j<manufacturer_size-1;j++)