Added partial support for FT230X
[libftdi] / src / ftdi.c
index 5a3d44e..e2c4009 100644 (file)
@@ -2,7 +2,7 @@
                           ftdi.c  -  description
                              -------------------
     begin                : Fri Apr 4 2003
-    copyright            : (C) 2003-2011 by Intra2net AG and the libftdi developers
+    copyright            : (C) 2003-2013 by Intra2net AG and the libftdi developers
     email                : opensource@intra2net.com
  ***************************************************************************/
 
@@ -470,7 +470,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 || ftdi->type == TYPE_232H )
+    if (ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H || ftdi->type == TYPE_232H || ftdi->type == TYPE_230X)
         packet_size = 512;
     else
         packet_size = 64;
@@ -608,6 +608,8 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
         ftdi->type = TYPE_4232H;
     else if (desc.bcdDevice == 0x900)
         ftdi->type = TYPE_232H;
+    else if (desc.bcdDevice == 0x1000)
+        ftdi->type = TYPE_230X;
 
     // Determine maximum packet size
     ftdi->max_packet_size = _ftdi_determine_max_packet_size(ftdi, dev);
@@ -1173,7 +1175,7 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
 
 #define H_CLK 120000000
 #define C_CLK  48000000
-    if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H) || (ftdi->type == TYPE_232H ))
+    if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H) || (ftdi->type == TYPE_232H) || (ftdi->type == TYPE_230X))
     {
         if(baudrate*10 > H_CLK /0x3fff)
         {
@@ -1198,8 +1200,7 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
     }
     // Split into "value" and "index" values
     *value = (unsigned short)(encoded_divisor & 0xFFFF);
-    if (ftdi->type == TYPE_2232H || 
-        ftdi->type == TYPE_4232H || ftdi->type == TYPE_232H )
+    if (ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H || ftdi->type == TYPE_232H || ftdi->type == TYPE_230X)
     {
         *index = (unsigned short)(encoded_divisor >> 8);
         *index &= 0xFF00;
@@ -1542,7 +1543,7 @@ struct ftdi_transfer_control *ftdi_write_data_submit(struct ftdi_context *ftdi,
     tc->size = size;
     tc->offset = 0;
 
-    if (size < ftdi->writebuffer_chunksize)
+    if (size < (int)ftdi->writebuffer_chunksize)
         write_size = size;
     else
         write_size = ftdi->writebuffer_chunksize;
@@ -1595,7 +1596,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);
 
@@ -1759,7 +1760,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);
 
@@ -2315,8 +2316,11 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
         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
@@ -2354,6 +2358,7 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
         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");
         }
@@ -2382,6 +2387,15 @@ 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] = CBUSH_TXDEN;
+        eeprom->cbus_function[1] = CBUSH_RXLED;
+        eeprom->cbus_function[2] = CBUSH_TXLED;
+        eeprom->cbus_function[3] = CBUSH_SLEEP;
+    }
     else
     {
         if(ftdi->type == TYPE_232H)
@@ -2392,9 +2406,83 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
         }
         eeprom->size = -1;
     }
-    eeprom->initialized_for_connected_device = 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 oprtions*/
 void set_ft232h_cbus(struct ftdi_eeprom *eeprom, unsigned char * output)
 {
@@ -2409,9 +2497,9 @@ void set_ft232h_cbus(struct ftdi_eeprom *eeprom, unsigned char * output)
         if (eeprom->cbus_function[2*i+1]> CBUSH_CLK7_5)
             mode_high = CBUSH_TRISTATE;
         else
-            mode_high = eeprom->cbus_function[2*i];
+            mode_high = eeprom->cbus_function[2*i+1];
 
-        output[0x18+i] = mode_high <<4 | mode_low;
+        output[0x18+i] = (mode_high <<4) | mode_low;
     }
 }
 /* Return the bits for the encoded EEPROM Structure of a requested Mode
@@ -2445,6 +2533,7 @@ static unsigned char type2bit(unsigned char type, enum ftdi_chip_type chip)
         default: return 0;
         }
     }
+    case TYPE_230X: /* FT230X is only UART */
     default: return 0;
     }
     return 0;
@@ -2484,10 +2573,12 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     if (eeprom->chip == -1)
         ftdi_error_return(-6,"No connected EEPROM or EEPROM type unknown");
 
-    if ((eeprom->chip == 0x56) || (eeprom->chip == 0x66))
-        eeprom->size = 0x100;
-    else
-        eeprom->size = 0x80;
+    if (eeprom->size == -1) {
+        if ((eeprom->chip == 0x56) || (eeprom->chip == 0x66))
+            eeprom->size = 0x100;
+        else
+            eeprom->size = 0x80;
+    }
 
     if (eeprom->manufacturer != NULL)
         manufacturer_size = strlen(eeprom->manufacturer);
@@ -2507,6 +2598,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             user_area_size = 90;     // two extra config bytes and 4 bytes PnP stuff
             break;
         case TYPE_R:
+        case TYPE_230X:
             user_area_size = 88;     // four extra config bytes + 4 bytes PnP stuff
             break;
         case TYPE_2232H:            // six extra config bytes + 4 bytes PnP stuff
@@ -2526,7 +2618,14 @@ 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
 
@@ -2539,33 +2638,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;
-        case TYPE_232H:
-            output[0x07] = 0x09;
-            break;
-        default:
-            output[0x07] = 0x00;
-    }
+    output[0x06] = eeprom->release_number;
+    output[0x07] = eeprom->release_number >> 8;
 
     // Addr 08: Config descriptor
     // Bit 7: always 1
@@ -2573,16 +2647,16 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
     // Bit 5: 1 if this device uses remote wakeup
     // Bit 4-0: reserved - 0
     j = 0x80;
-    if (eeprom->self_powered == 1)
+    if (eeprom->self_powered)
         j |= 0x40;
-    if (eeprom->remote_wakeup == 1)
+    if (eeprom->remote_wakeup)
         j |= 0x20;
     output[0x08] = j;
 
     // Addr 09: Max power consumption: max power = value * 2 mA
     output[0x09] = eeprom->max_power / 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
@@ -2595,9 +2669,9 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
         // Bit 0: 1 - In EndPoint is Isochronous
         //
         j = 0;
-        if (eeprom->in_is_isochronous == 1)
+        if (eeprom->in_is_isochronous)
             j = j | 1;
-        if (eeprom->out_is_isochronous == 1)
+        if (eeprom->out_is_isochronous)
             j = j | 2;
         output[0x0A] = j;
     }
@@ -2621,6 +2695,10 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
         case TYPE_AM:
         case TYPE_BM:
             i += 0x94;
+            break;
+        case TYPE_230X:
+            i = 0xa0;
+            break;
     }
     /* Wrap around 0x80 for 128 byte EEPROMS (Internale and 93x46) */
     eeprom_size_mask = eeprom->size -1;
@@ -2721,15 +2799,15 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             else
                 output[0x01] &= ~HIGH_CURRENT_DRIVE;
 
-            if (eeprom->in_is_isochronous == 1)
+            if (eeprom->in_is_isochronous)
                 output[0x0A] |= 0x1;
             else
                 output[0x0A] &= ~0x1;
-            if (eeprom->out_is_isochronous == 1)
+            if (eeprom->out_is_isochronous)
                 output[0x0A] |= 0x2;
             else
                 output[0x0A] &= ~0x2;
-            if (eeprom->suspend_pull_downs == 1)
+            if (eeprom->suspend_pull_downs)
                 output[0x0A] |= 0x4;
             else
                 output[0x0A] &= ~0x4;
@@ -2747,7 +2825,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
                 output[0x00] |= HIGH_CURRENT_DRIVE_R;
             output[0x01] = 0x40; /* Hard coded Endpoint Size*/
 
-            if (eeprom->suspend_pull_downs == 1)
+            if (eeprom->suspend_pull_downs)
                 output[0x0A] |= 0x4;
             else
                 output[0x0A] &= ~0x4;
@@ -2797,7 +2875,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             else
                 output[0x01] &= ~SUSPEND_DBUS7_BIT;
 
-            if (eeprom->suspend_pull_downs == 1)
+            if (eeprom->suspend_pull_downs)
                 output[0x0A] |= 0x4;
             else
                 output[0x0A] &= ~0x4;
@@ -2859,7 +2937,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             else
                 output[0x01] &= ~(DRIVER_VCP << 4);
 
-            if (eeprom->suspend_pull_downs == 1)
+            if (eeprom->suspend_pull_downs)
                 output[0x0a] |= 0x4;
             else
                 output[0x0a] &= ~0x4;
@@ -2965,7 +3043,14 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             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 */
+            output[0x0c] = (0x01) | (0x3 << 4); /* DBUS drive 4mA, CBUS drive 16mA */
+            for (j = 0; j <= 6; j++) {
+                output[0x1a + j] = eeprom->cbus_function[j];
+            }
+            break;
     }
 
     // calculate checksum
@@ -2973,6 +3058,10 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
     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 = output[i*2];
         value += output[(i*2)+1] << 8;
 
@@ -2983,6 +3072,7 @@ 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 
@@ -3024,8 +3114,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     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");
@@ -3034,6 +3123,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 
     eeprom = ftdi->eeprom;
     eeprom_size = eeprom->size;
+    buf = ftdi->eeprom->buf;
 
     // Addr 02: Vendor ID
     eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8);
@@ -3041,7 +3131,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
@@ -3141,6 +3232,10 @@ 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;
 
@@ -3256,13 +3351,25 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
         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;
+    }
 
     if (verbose)
     {
         char *channel_mode[] = {"UART", "FIFO", "CPU", "OPTO", "FT1284"};
         fprintf(stdout, "VID:     0x%04x\n",eeprom->vendor_id);
         fprintf(stdout, "PID:     0x%04x\n",eeprom->product_id);
-        fprintf(stdout, "Release: 0x%04x\n",release);
+        fprintf(stdout, "Release: 0x%04x\n",eeprom->release_number);
 
         if (eeprom->self_powered)
             fprintf(stdout, "Self-Powered%s", (eeprom->remote_wakeup)?", USB Remote Wake Up\n":"\n");
@@ -3358,6 +3465,29 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
                             cbush_mux[eeprom->cbus_function[i]]);
             }
         }
+        else if (ftdi->type == TYPE_230X)
+        {
+            int i;
+            char *cbush_mux[] = {"TRISTATE","RXLED","TXLED", "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,"IOBUS 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]<= CBUSH_AWAKE)
+                    fprintf(stdout,"CBUS%d Function: %s\n", i, cbush_mux[eeprom->cbus_function[i]]);
+            }
+        }
 
         if (ftdi->type == TYPE_R)
         {
@@ -3417,6 +3547,9 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case PRODUCT_ID:
             *value = ftdi->eeprom->product_id;
             break;
+        case RELEASE_NUMBER:
+            *value = ftdi->eeprom->release_number;
+            break;
         case SELF_POWERED:
             *value = ftdi->eeprom->self_powered;
             break;
@@ -3604,6 +3737,9 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case PRODUCT_ID:
             ftdi->eeprom->product_id = value;
             break;
+        case RELEASE_NUMBER:
+            ftdi->eeprom->release_number = value;
+            break;
         case SELF_POWERED:
             ftdi->eeprom->self_powered = value;
             break;
@@ -3765,6 +3901,7 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         default :
             ftdi_error_return(-1, "Request to unknown EEPROM value");
     }
+    ftdi->eeprom->initialized_for_connected_device = 0;
     return 0;
 }
 
@@ -4026,6 +4163,10 @@ 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,