Decode the x232H specific port functions
[libftdi] / src / ftdi.c
index 36bcf3b..355ea58 100644 (file)
@@ -2192,7 +2192,7 @@ void ftdi_eeprom_initdefaults(struct ftdi_context *ftdi)
 
     eeprom->self_powered = 1;
     eeprom->remote_wakeup = 1;
-    eeprom->chip_type = TYPE_BM;
+    eeprom->release = 0;
 
     eeprom->in_is_isochronous = 0;
     eeprom->out_is_isochronous = 0;
@@ -2210,7 +2210,7 @@ void ftdi_eeprom_initdefaults(struct ftdi_context *ftdi)
     {
         eeprom->cbus_function[i] = 0;
     }
-    eeprom->high_current = 0;
+    eeprom->high_current_a = 0;
     eeprom->invert = 0;
 
     eeprom->size = FTDI_MAX_EEPROM_SIZE;
@@ -2286,12 +2286,12 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
     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;
+            (eeprom->cbus_function[i] && ftdi->type != TYPE_R)) return -3;
     }
-    if (eeprom->chip_type != TYPE_R)
+    if (ftdi->type != TYPE_R)
     {
         if (eeprom->invert) return -4;
-        if (eeprom->high_current) return -5;
+        if (eeprom->high_current_a) return -5;
     }
 
     size_check = eeprom->size;
@@ -2314,9 +2314,9 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
     memset (output, 0, eeprom->size);
 
     // Addr 00: High current IO
-    output[0x00] = eeprom->high_current ? HIGH_CURRENT_DRIVE : 0;
+    output[0x00] = eeprom->high_current_a ? HIGH_CURRENT_DRIVE : 0;
     // Addr 01: IN endpoint size (for R type devices, different for FT2232)
-    if (eeprom->chip_type == TYPE_R) {
+    if (ftdi->type == TYPE_R) {
         output[0x01] = 0x40;
     }
     // Addr 02: Vendor ID
@@ -2329,7 +2329,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->chip_type) {
+    switch (eeprom->release) {
         case TYPE_AM:
             output[0x07] = 0x02;
             break;
@@ -2419,7 +2419,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
     // Dynamic content
     // In images produced by FTDI's FT_Prog for FT232R strings start at 0x18
     // Space till 0x18 should be considered as reserved.
-    if (eeprom->chip_type >= TYPE_R) {
+    if (ftdi->type >= TYPE_R) {
         i = 0x18;
     } else {
         i = 0x14;
@@ -2488,43 +2488,35 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output)
    FIXME: How to pass size? How to handle size field in ftdi_eeprom?
    FIXME: Strings are malloc'ed here and should be freed somewhere
 */
-int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size)
+int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size, int verbose)
 {
     unsigned char i, j;
     unsigned short checksum, eeprom_checksum, value;
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
-    int eeprom_size = 128;
+    int eeprom_size;
     struct ftdi_eeprom *eeprom;
 
     if (ftdi == NULL)
         ftdi_error_return(-1,"No context");
     if (ftdi->eeprom == NULL)
         ftdi_error_return(-1,"No eeprom");
-
+    eeprom_size = ftdi->eeprom->size;
+    if(ftdi->type == TYPE_R)
+        eeprom_size = 0x80;
     eeprom = ftdi->eeprom;
-#if 0
-    size_check = eeprom->size;
-    size_check -= 28; // 28 are always in use (fixed)
 
-    // Top half of a 256byte eeprom is used just for strings and checksum
-    // it seems that the FTDI chip will not read these strings from the lower half
-    // Each string starts with two bytes; offset and type (0x03 for string)
-    // the checksum needs two bytes, so without the string data that 8 bytes from the top half
-    if (eeprom->size>=256)size_check = 120;
-    size_check -= manufacturer_size*2;
-    size_check -= product_size*2;
-    size_check -= serial_size*2;
+    // Addr 00: Channel A setting
 
-    // eeprom size exceeded?
-    if (size_check < 0)
-        ftdi_error_return(-1,"Size check failed");
-#endif
+    eeprom->channel_a_type   = buf[0x00] & 0x7;
+    eeprom->channel_a_driver = buf[0x00] & DRIVER_VCP;
+    eeprom->high_current_a   = buf[0x00] & HIGH_CURRENT_DRIVE;
 
-    // empty eeprom struct
-    memset(eeprom, 0, sizeof(struct ftdi_eeprom));
+    // Addr 01: Channel B setting
 
-    // Addr 00: High current IO
-    eeprom->high_current = (buf[0x02] & 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;
 
     // Addr 02: Vendor ID
     eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8);
@@ -2532,31 +2524,15 @@ 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);
 
-    value = buf[0x06] + (buf[0x07]<<8);
-    switch (value)
-    {
-        case 0x0600:
-            eeprom->chip_type = TYPE_R;
-            break;
-        case 0x0400:
-            eeprom->chip_type = TYPE_BM;
-            break;
-        case 0x0200:
-            eeprom->chip_type = TYPE_AM;
-            break;
-        default: // Unknown device
-            eeprom->chip_type = 0;
-            break;
-    }
+    eeprom->release = buf[0x06] + (buf[0x07]<<8);
 
     // Addr 08: Config descriptor
     // Bit 7: always 1
     // Bit 6: 1 if this device is self powered, 0 if bus powered
     // Bit 5: 1 if this device uses remote wakeup
     // Bit 4: 1 if this device is battery powered
-    j = buf[0x08];
-    if (j&0x40) eeprom->self_powered = 1;
-    if (j&0x20) eeprom->remote_wakeup = 1;
+    eeprom->self_powered = buf[0x08] & 0x40;
+    eeprom->remote_wakeup = buf[0x08] & 0x20;;
 
     // Addr 09: Max power consumption: max power = value * 2 mA
     eeprom->max_power = buf[0x09];
@@ -2566,24 +2542,22 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size)
     // Bit 6: 0 - reserved
     // Bit 5: 0 - reserved
     // Bit 4: 1 - Change USB version
+    //            Not seen on FT2232(D)
     // Bit 3: 1 - Use the serial number string
     // Bit 2: 1 - Enable suspend pull downs for lower power
     // Bit 1: 1 - Out EndPoint is Isochronous
     // Bit 0: 1 - In EndPoint is Isochronous
     //
-    j = buf[0x0A];
-    if (j&0x01) eeprom->in_is_isochronous = 1;
-    if (j&0x02) eeprom->out_is_isochronous = 1;
-    if (j&0x04) eeprom->suspend_pull_downs = 1;
-    if (j&0x08) eeprom->use_serial = 1;
-    if (j&0x10) eeprom->change_usb_version = 1;
+    eeprom->in_is_isochronous  = buf[0x0A]&0x01;
+    eeprom->out_is_isochronous = buf[0x0A]&0x02;
+    eeprom->suspend_pull_downs = buf[0x0A]&0x04;
+    eeprom->use_serial         = buf[0x0A]&0x08;
+    eeprom->change_usb_version = buf[0x0A]&0x10;
 
-    // Addr 0B: Invert data lines
-    eeprom->invert = buf[0x0B];
 
     // Addr 0C: USB version low byte when 0x0A bit 4 is set
     // Addr 0D: USB version high byte when 0x0A bit 4 is set
-    if (eeprom->change_usb_version == 1)
+    if ((eeprom->change_usb_version == 1) || ftdi->type == TYPE_2232C)
     {
         eeprom->usb_version = buf[0x0C] + (buf[0x0D] << 8);
     }
@@ -2591,57 +2565,59 @@ 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 (manufacturer_size > 0) eeprom->manufacturer = malloc(manufacturer_size);
+    if (manufacturer_size > 0) 
+    {
+        eeprom->manufacturer = malloc(manufacturer_size);
+        if (eeprom->manufacturer)
+        {
+            // Decode manufacturer
+            i = buf[0x0E] & (eeprom_size -1); // offset
+            for (j=0;j<manufacturer_size-1;j++)
+            {
+                eeprom->manufacturer[j] = buf[2*j+i+2];
+            }
+            eeprom->manufacturer[j] = '\0';
+        }
+    }
     else eeprom->manufacturer = NULL;
 
     // Addr 10: Offset of the product string + 0x80, calculated later
     // Addr 11: Length of product string
     product_size = buf[0x11]/2;
-    if (product_size > 0) eeprom->product = malloc(product_size);
+    if (product_size > 0)
+    {
+        eeprom->product = malloc(product_size);
+        if(eeprom->product)
+        {
+            // Decode product name
+            i = buf[0x10] & (eeprom_size -1); // offset
+            for (j=0;j<product_size-1;j++)
+            {
+                eeprom->product[j] = buf[2*j+i+2];
+            }
+            eeprom->product[j] = '\0';
+        }
+    }
     else eeprom->product = NULL;
 
     // Addr 12: Offset of the serial string + 0x80, calculated later
     // Addr 13: Length of serial string
     serial_size = buf[0x13]/2;
-    if (serial_size > 0) eeprom->serial = malloc(serial_size);
-    else eeprom->serial = NULL;
-
-    // Addr 14: CBUS function: CBUS0, CBUS1
-    // Addr 15: CBUS function: CBUS2, CBUS3
-    // Addr 16: CBUS function: CBUS5
-    if (eeprom->chip_type == TYPE_R) {
-        eeprom->cbus_function[0] = buf[0x14] & 0x0f;
-        eeprom->cbus_function[1] = (buf[0x14] >> 4) & 0x0f;
-        eeprom->cbus_function[2] = buf[0x15] & 0x0f;
-        eeprom->cbus_function[3] = (buf[0x15] >> 4) & 0x0f;
-        eeprom->cbus_function[4] = buf[0x16] & 0x0f;
-    } else {
-        for (j=0; j<5; j++) eeprom->cbus_function[j] = 0;
-    }
-
-    // Decode manufacturer
-    i = buf[0x0E] & 0x7f; // offset
-    for (j=0;j<manufacturer_size-1;j++)
-    {
-        eeprom->manufacturer[j] = buf[2*j+i+2];
-    }
-    eeprom->manufacturer[j] = '\0';
-
-    // Decode product name
-    i = buf[0x10] & 0x7f; // offset
-    for (j=0;j<product_size-1;j++)
+    if (serial_size > 0)
     {
-        eeprom->product[j] = buf[2*j+i+2];
-    }
-    eeprom->product[j] = '\0';
-
-    // Decode serial
-    i = buf[0x12] & 0x7f; // offset
-    for (j=0;j<serial_size-1;j++)
-    {
-        eeprom->serial[j] = buf[2*j+i+2];
+        eeprom->serial = malloc(serial_size);
+        if(eeprom->serial)
+        {
+            // Decode serial
+            i = buf[0x12] & (eeprom_size -1); // offset
+            for (j=0;j<serial_size-1;j++)
+            {
+                eeprom->serial[j] = buf[2*j+i+2];
+            }
+            eeprom->serial[j] = '\0';
+        }
     }
-    eeprom->serial[j] = '\0';
+    else eeprom->serial = NULL;
 
     // verify checksum
     checksum = 0xAAAA;
@@ -2663,6 +2639,99 @@ 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->chip = buf[14];
+    }
+    else if(ftdi->type == TYPE_2232C)
+    {
+        eeprom->chip = buf[14];
+    }
+    else if(ftdi->type == TYPE_R)
+    {
+        // Addr 0B: Invert data lines
+        // Works only on FT232R, not FT245R, but no way to distinguish
+        eeprom->invert = buf[0x0B];
+        // Addr 14: CBUS function: CBUS0, CBUS1
+        // Addr 15: CBUS function: CBUS2, CBUS3
+        // Addr 16: CBUS function: CBUS5
+        eeprom->cbus_function[0] = buf[0x14] & 0x0f;
+        eeprom->cbus_function[1] = (buf[0x14] >> 4) & 0x0f;
+        eeprom->cbus_function[2] = buf[0x15] & 0x0f;
+        eeprom->cbus_function[3] = (buf[0x15] >> 4) & 0x0f;
+        eeprom->cbus_function[4] = buf[0x16] & 0x0f;
+    }
+    else if ((ftdi->type == TYPE_2232H) ||(ftdi->type == TYPE_4232H)) 
+    {
+        eeprom->group0_drive   =  buf[0x0c]       & DRIVE_16MA;
+        eeprom->group0_schmitt =  buf[0x0c]       & IS_SCHMITT;
+        eeprom->group0_slew    =  buf[0x0c]       & SLOW_SLEW;
+        eeprom->group1_drive   = (buf[0x0c] >> 4) & 0x3;
+        eeprom->group1_schmitt = (buf[0x0c] >> 4) & IS_SCHMITT;
+        eeprom->group1_slew    = (buf[0x0c] >> 4) & SLOW_SLEW;
+        eeprom->group2_drive   =  buf[0x0d]       & DRIVE_16MA;
+        eeprom->group2_schmitt =  buf[0x0d]       & IS_SCHMITT;
+        eeprom->group2_slew    =  buf[0x0d]       & SLOW_SLEW;
+        eeprom->group3_drive   = (buf[0x0d] >> 4) & DRIVE_16MA;
+        eeprom->group3_schmitt = (buf[0x0d] >> 4) & IS_SCHMITT;
+        eeprom->group3_slew    = (buf[0x0d] >> 4) & SLOW_SLEW;
+    }
+    
+    if(verbose)
+    {
+        char *channel_mode[] = {"UART","245","CPU", "unknown", "OPTO"};
+        fprintf(stdout, "VID:     0x%04x\n",eeprom->vendor_id);
+        fprintf(stdout, "PID:     0x%04x\n",eeprom->product_id);
+        fprintf(stdout, "Release: 0x%04x\n",eeprom->release);
+
+        if(eeprom->self_powered)
+            fprintf(stdout, "Self-Powered%s", (eeprom->remote_wakeup)?", USB Remote Wake Up\n":"\n");
+        else
+            fprintf(stdout, "Bus Powered: %3d mA%s", eeprom->max_power*2,
+                    (eeprom->remote_wakeup)?" USB Remote Wake Up\n":"\n");
+        if(eeprom->manufacturer)
+            fprintf(stdout, "Manufacturer: %s\n",eeprom->manufacturer);
+        if(eeprom->product)
+            fprintf(stdout, "Product:      %s\n",eeprom->product);
+        if(eeprom->serial)
+            fprintf(stdout, "Serial:       %s\n",eeprom->serial);
+        fprintf(stdout,     "Checksum      : %04x\n", checksum);
+        if (ftdi->type >= TYPE_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)
+            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":"");
+        if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H)) 
+        {
+            fprintf(stdout,"%s has %d mA drive%s%s\n",
+                    (ftdi->type == TYPE_2232H)?"AL":"A",
+                    (eeprom->group0_drive+1) *4,
+                    (eeprom->group0_schmitt)?" Schmitt Input":"",
+                    (eeprom->group0_slew)?" Slow Slew":"");
+            fprintf(stdout,"%s has %d mA drive%s%s\n",
+                    (ftdi->type == TYPE_2232H)?"AH":"B",
+                    (eeprom->group1_drive+1) *4,
+                    (eeprom->group1_schmitt)?" Schmitt Input":"",
+                    (eeprom->group1_slew)?" Slow Slew":"");
+            fprintf(stdout,"%s has %d mA drive%s%s\n",
+                    (ftdi->type == TYPE_2232H)?"BL":"C",
+                    (eeprom->group2_drive+1) *4,
+                    (eeprom->group2_schmitt)?" Schmitt Input":"",
+                    (eeprom->group2_slew)?" Slow Slew":"");
+            fprintf(stdout,"%s has %d mA drive%s%s\n",
+                    (ftdi->type == TYPE_2232H)?"BH":"D",
+                    (eeprom->group3_drive+1) *4,
+                    (eeprom->group3_schmitt)?" Schmitt Input":"",
+                    (eeprom->group3_slew)?" Slow Slew":"");
+        }
+
+    }
+
     return 0;
 }