X-Git-Url: http://developer.intra2net.com/git/?p=libftdi;a=blobdiff_plain;f=src%2Fftdi.c;h=65a626ffa79c589d63044cc9cf914723989e7e7a;hp=5a27ac3ce3cfc0262a9a8f967bc935910375c896;hb=2b9a3c8226573d507149b8ca6e5a925229dd691d;hpb=f2cd9fd5d8cac4ca8c94e4f5e6560b4967c87be5 diff --git a/src/ftdi.c b/src/ftdi.c index 5a27ac3..65a626f 100644 --- a/src/ftdi.c +++ b/src/ftdi.c @@ -2186,34 +2186,37 @@ void ftdi_eeprom_initdefaults(struct ftdi_context *ftdi) return; eeprom = ftdi->eeprom; + memset(eeprom, 0, sizeof(struct ftdi_eeprom)); eeprom->vendor_id = 0x0403; - eeprom->product_id = 0x6001; - - eeprom->self_powered = 1; - eeprom->remote_wakeup = 1; - eeprom->chip_type = TYPE_BM; - - eeprom->in_is_isochronous = 0; - eeprom->out_is_isochronous = 0; - eeprom->suspend_pull_downs = 0; - - eeprom->use_serial = 0; - eeprom->change_usb_version = 0; + eeprom->use_serial = USE_SERIAL_NUM; + if((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM) || + (ftdi->type == TYPE_R)) + 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; + } eeprom->usb_version = 0x0200; - eeprom->max_power = 0; + eeprom->max_power = 100; 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_MAX_EEPROM_SIZE; + if(ftdi->type == TYPE_R) + eeprom->size = 0x80; + else + eeprom->size = -1; } /** @@ -2258,6 +2261,7 @@ void ftdi_eeprom_free(struct ftdi_context *ftdi) \retval -3: Invalid cbus function setting \retval -4: Chip doesn't support invert \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) { @@ -2275,6 +2279,9 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi, unsigned char *output) eeprom= ftdi->eeprom; + if(eeprom->chip == -1) + ftdi_error_return(-5,"No connected EEPROM or EEPROM Type unknown"); + if (eeprom->manufacturer != NULL) manufacturer_size = strlen(eeprom->manufacturer); if (eeprom->product != NULL) @@ -2286,26 +2293,36 @@ 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; - size_check -= 28; // 28 are always in use (fixed) + size_check = 0x80; + switch(ftdi->type) + { + case TYPE_2232H: + case TYPE_4232H: + size_check -= 4; + case TYPE_R: + size_check -= 4; + case TYPE_2232C: + size_check -= 4; + case TYPE_AM: + case TYPE_BM: + size_check -= 0x14*2; + } - // 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; + /* Space for the string type and pointer bytes */ + size_check -= -6; + // eeprom size exceeded? if (size_check < 0) return (-1); @@ -2314,9 +2331,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 +2346,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 +2436,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,7 +2505,7 @@ 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; @@ -2499,35 +2516,26 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, unsigned char *buf, int size) if (ftdi == NULL) ftdi_error_return(-1,"No context"); if (ftdi->eeprom == NULL) - ftdi_error_return(-1,"No eeprom"); + ftdi_error_return(-1,"No eeprom structure"); 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; - // eeprom size exceeded? - if (size_check < 0) - ftdi_error_return(-1,"Size check failed"); -#endif + // Addr 00: Channel A setting - // empty eeprom struct - memset(eeprom, 0, sizeof(struct ftdi_eeprom)); + eeprom->channel_a_type = buf[0x00] & 0x7; + eeprom->channel_a_driver = buf[0x00] & DRIVER_VCP; + eeprom->high_current_a = buf[0x00] & HIGH_CURRENT_DRIVE; - // Addr 00: High current IO - eeprom->high_current = (buf[0x02] & 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; // Addr 02: Vendor ID eeprom->vendor_id = buf[0x02] + (buf[0x03] << 8); @@ -2535,31 +2543,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]; @@ -2569,24 +2561,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] & USE_SERIAL_NUM; + 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); } @@ -2594,57 +2584,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;jmanufacturer[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;jproduct[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;jmanufacturer[j] = buf[2*j+i+2]; - } - eeprom->manufacturer[j] = '\0'; - - // Decode product name - i = buf[0x10] & 0x7f; // offset - for (j=0;jproduct[j] = buf[2*j+i+2]; - } - eeprom->product[j] = '\0'; - - // Decode serial - i = buf[0x12] & 0x7f; // offset - for (j=0;j 0) { - 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;jserial[j] = buf[2*j+i+2]; + } + eeprom->serial[j] = '\0'; + } } - eeprom->serial[j] = '\0'; + else eeprom->serial = NULL; // verify checksum checksum = 0xAAAA; @@ -2666,6 +2658,111 @@ 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 = -1; + } + else if(ftdi->type == TYPE_2232C) + { + eeprom->chip = buf[0x14]; + } + else if(ftdi->type == TYPE_R) + { + eeprom->chip = buf[0x16]; + // 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->chip = buf[0x18]; + 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_R) + fprintf(stdout, "Internal EEPROM\n"); + 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) + fprintf(stdout, "Pull IO pins low during suspend\n"); + if(eeprom->remote_wakeup) + fprintf(stdout, "Enable Remote Wake Up\n"); + 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; } @@ -2853,15 +2950,61 @@ int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom) \retval 0: all fine \retval -1: erase failed \retval -2: USB device unavailable + \retval -3: Writing magic failed + \retval -4: Read EEPROM failed + \retval -5: Unexpected EEPROM value */ +#define MAGIC 0x55aa int ftdi_erase_eeprom(struct ftdi_context *ftdi) { + unsigned short eeprom_value; if (ftdi == NULL || ftdi->usb_dev == NULL) ftdi_error_return(-2, "USB device unavailable"); - if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_ERASE_EEPROM_REQUEST, 0, 0, NULL, 0, ftdi->usb_write_timeout) < 0) + if(ftdi->type == TYPE_R) + { + ftdi->eeprom->chip = 0; + return 0; + } + + 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( ftdi_write_eeprom_location(ftdi, 0xc0, MAGIC)) + 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) + { + ftdi->eeprom->chip = 0x46; + } + else + { + if (ftdi_read_eeprom_location( ftdi, 0x40, &eeprom_value)) + ftdi_error_return(-4, "Reading failed failed"); + if(eeprom_value == MAGIC) + ftdi->eeprom->chip = 0x56; + else + { + if (ftdi_read_eeprom_location( ftdi, 0xc0, &eeprom_value)) + ftdi_error_return(-4, "Reading failed failed"); + if(eeprom_value == MAGIC) + ftdi->eeprom->chip = 0x66; + else + { + ftdi->eeprom->chip = -1; + } + } + } + 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; }