\param ftdi pointer to ftdi_context
- \retval >0: free eeprom size
+ \retval >=0: size of eeprom user area in bytes
\retval -1: eeprom size (128 bytes) exceeded by custom strings
\retval -2: Invalid eeprom pointer
\retval -3: Invalid cbus function setting
*/
int ftdi_eeprom_build(struct ftdi_context *ftdi)
{
- unsigned char i, j, k;
+ 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;
+ int user_area_size;
struct ftdi_eeprom *eeprom;
unsigned char * output;
if (eeprom->serial != NULL)
serial_size = strlen(eeprom->serial);
- size_check = 0x80;
- switch(ftdi->type)
+ // eeprom size check
+ 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;
+ case TYPE_AM:
+ case TYPE_BM:
+ user_area_size = 96; // base size for strings (total of 48 characters)
+ break;
+ case TYPE_2232C:
+ user_area_size = 90; // two extra config bytes and 4 bytes PnP stuff
+ break;
+ case TYPE_R:
+ user_area_size = 88; // four extra config bytes + 4 bytes PnP stuff
+ break;
+ case TYPE_2232H: // six extra config bytes + 4 bytes PnP stuff
+ case TYPE_4232H:
+ user_area_size = 86;
+ break;
}
+ user_area_size -= (manufacturer_size + product_size + serial_size) * 2;
- 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 -= -9;
-
- // eeprom size exceeded?
- if (size_check < 0)
- return (-1);
+ if (user_area_size < 0)
+ ftdi_error_return(-1,"eeprom size exceeded");
// empty eeprom
memset (ftdi->eeprom->buf, 0, FTDI_MAX_EEPROM_SIZE);
// Addr 06: Device release number (0400h for BM features)
output[0x06] = 0x00;
- switch (ftdi->type) {
+ switch (ftdi->type)
+ {
case TYPE_AM:
output[0x07] = 0x02;
break;
// 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;
i += 0x94;
}
/* Wrap around 0x80 for 128 byte EEPROMS (Internale and 93x46) */
- k = eeprom->size -1;
+ 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++ & k] = manufacturer_size*2 + 2;
- output[i++ & k] = 0x03; // type: string
+ 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 & k] = eeprom->manufacturer[j], i++;
- output[i & k] = 0x00, i++;
+ 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 & k] = product_size*2 + 2, i++;
- output[i & k] = 0x03, i++;
+ 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 & k] = eeprom->product[j], i++;
- output[i & k] = 0x00, i++;
+ 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[0x12] = i | 0x80; // calculate offset
- output[i & k] = serial_size*2 + 2, i++;
- output[i & k] = 0x03, i++;
+ 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 & k] = eeprom->serial[j], i++;
- output[i & k] = 0x00, i++;
+ output[i & eeprom_size_mask] = eeprom->serial[j], i++;
+ output[i & eeprom_size_mask] = 0x00, i++;
+ }
+
+ // Legacy port name and PnP fields for FT2232 and newer chips
+ if (ftdi->type > TYPE_BM)
+ {
+ 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[i & k] = 0x02; /* as seen when written with FTD2XX */
- i++;
- output[i & k] = 0x03; /* as seen when written with FTD2XX */
- i++;
- output[i & k] = eeprom->is_not_pnp; /* as seen when written with FTD2XX */
- i++;
output[0x13] = serial_size*2 + 2;
- if(ftdi->type > TYPE_AM) /*use_serial not used in AM devices*/
+ if(ftdi->type > TYPE_AM) /* use_serial not used in AM devices */
{
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
/* Bytes and Bits specific to (some) types
Write linear, as this allows easier fixing*/
case TYPE_BM:
output[0x0C] = eeprom->usb_version & 0xff;
output[0x0D] = (eeprom->usb_version>>8) & 0xff;
- output[0x14] = eeprom->chip;
+ if (eeprom->use_usb_version == USE_USB_VERSION_BIT)
+ output[0x0A] |= USE_USB_VERSION_BIT;
+ else
+ output[0x0A] &= ~USE_USB_VERSION_BIT;
+
break;
case TYPE_2232C:
output[0x0A] |= 0x4;
else
output[0x0A] &= ~0x4;
+ if (eeprom->use_usb_version == USE_USB_VERSION_BIT)
+ output[0x0A] |= USE_USB_VERSION_BIT;
+ else
+ output[0x0A] &= ~USE_USB_VERSION_BIT;
+
output[0x0C] = eeprom->usb_version & 0xff;
output[0x0D] = (eeprom->usb_version>>8) & 0xff;
output[0x14] = eeprom->chip;
output[0x01] |= DRIVER_VCP;
else
output[0x01] &= ~DRIVER_VCP;
- if(eeprom->suspend_dbus7 == SUSPEND_DBUS7)
- output[0x01] |= SUSPEND_DBUS7;
+ if(eeprom->suspend_dbus7 == SUSPEND_DBUS7_BIT)
+ output[0x01] |= SUSPEND_DBUS7_BIT;
else
- output[0x01] &= ~SUSPEND_DBUS7;
+ output[0x01] &= ~SUSPEND_DBUS7_BIT;
if (eeprom->suspend_pull_downs == 1)
output[0x0A] |= 0x4;
output[eeprom->size-2] = checksum;
output[eeprom->size-1] = checksum >> 8;
- return size_check;
+ return user_area_size;
}
/**
// 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;;
+ eeprom->remote_wakeup = buf[0x08] & 0x20;
// Addr 09: Max power consumption: max power = value * 2 mA
eeprom->max_power = buf[0x09];
// Bit 7: 0 - reserved
// Bit 6: 0 - reserved
// Bit 5: 0 - reserved
- // Bit 4: 1 - Change USB version
- // Not seen on FT2232(D)
+ // Bit 4: 1 - Change USB version on BM and 2232C
// 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
eeprom->out_is_isochronous = buf[0x0A]&0x02;
eeprom->suspend_pull_downs = buf[0x0A]&0x04;
eeprom->use_serial = buf[0x0A] & USE_SERIAL_NUM;
- if(buf[0x0A]&0x10)
- fprintf(stderr,
- "EEPROM byte[0x0a] Bit 4 unexpected set. If this happened with the EEPROM\n"
- "programmed by FTDI tools, please report to libftdi@developer.intra2net.com\n");
-
+ eeprom->use_usb_version = buf[0x0A] & USE_USB_VERSION_BIT;
// Addr 0C: USB version low byte when 0x0A
// Addr 0D: USB version high byte when 0x0A
eeprom->channel_b_driver = buf[0x01] & DRIVER_VCP;
if(ftdi->type == TYPE_2232H)
- eeprom->suspend_dbus7 = buf[0x01] & SUSPEND_DBUS7;
+ eeprom->suspend_dbus7 = buf[0x01] & SUSPEND_DBUS7_BIT;
eeprom->chip = buf[0x18];
eeprom->group0_drive = buf[0x0c] & DRIVE_16MA;
channel_mode[eeprom->channel_b_type],
(eeprom->channel_b_driver)?" VCP":"",
(eeprom->high_current_b)?" High Current IO":"");
+ if (((ftdi->type == TYPE_BM) || (ftdi->type == TYPE_2232C)) &&
+ eeprom->use_usb_version == USE_USB_VERSION_BIT)
+ fprintf(stdout,"Use explicit USB Version %04x\n",eeprom->usb_version);
+
if ((ftdi->type == TYPE_2232H) || (ftdi->type == TYPE_4232H))
{
fprintf(stdout,"%s has %d mA drive%s%s\n",
}
/**
+ Get a value from the decoded EEPROM structure
+
+ \\param ftdi pointer to ftdi_context
+ \\param value_name Enum of the value to query
+ \\param Pointer to store read value
+
+ \\retval 0: all fine
+ \\retval -1: Value doesn't exist
+*/
+int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int* value)
+{
+ switch (value_name)
+ {
+ case VENDOR_ID:
+ *value = ftdi->eeprom->vendor_id;
+ break;
+ case PRODUCT_ID:
+ *value = ftdi->eeprom->product_id;
+ break;
+ case SELF_POWERED:
+ *value = ftdi->eeprom->self_powered;
+ break;
+ case REMOTE_WAKEUP:
+ *value = ftdi->eeprom->remote_wakeup;
+ break;
+ case IS_NOT_PNP:
+ *value = ftdi->eeprom->is_not_pnp;
+ break;
+ case SUSPEND_DBUS7:
+ *value = ftdi->eeprom->suspend_dbus7;
+ break;
+ case IN_IS_ISOCHRONOUS:
+ *value = ftdi->eeprom->in_is_isochronous;
+ break;
+ case SUSPEND_PULL_DOWNS:
+ *value = ftdi->eeprom->suspend_pull_downs;
+ break;
+ case USE_SERIAL:
+ *value = ftdi->eeprom->use_serial;
+ break;
+ case USB_VERSION:
+ *value = ftdi->eeprom->usb_version;
+ break;
+ case MAX_POWER:
+ *value = ftdi->eeprom->max_power;
+ break;
+ case CHANNEL_A_TYPE:
+ *value = ftdi->eeprom->channel_a_type;
+ break;
+ case CHANNEL_B_TYPE:
+ *value = ftdi->eeprom->channel_b_type;
+ break;
+ case CHANNEL_A_DRIVER:
+ *value = ftdi->eeprom->channel_a_driver;
+ break;
+ case CHANNEL_B_DRIVER:
+ *value = ftdi->eeprom->channel_b_driver;
+ break;
+ case CBUS_FUNCTION_0:
+ *value = ftdi->eeprom->cbus_function[0];
+ break;
+ case CBUS_FUNCTION_1:
+ *value = ftdi->eeprom->cbus_function[1];
+ break;
+ case CBUS_FUNCTION_2:
+ *value = ftdi->eeprom->cbus_function[2];
+ break;
+ case CBUS_FUNCTION_3:
+ *value = ftdi->eeprom->cbus_function[3];
+ break;
+ case CBUS_FUNCTION_4:
+ *value = ftdi->eeprom->cbus_function[4];
+ break;
+ case HIGH_CURRENT:
+ *value = ftdi->eeprom->high_current;
+ break;
+ case HIGH_CURRENT_A:
+ *value = ftdi->eeprom->high_current_a;
+ break;
+ case HIGH_CURRENT_B:
+ *value = ftdi->eeprom->high_current_b;
+ break;
+ case INVERT:
+ *value = ftdi->eeprom->invert;
+ break;
+ case GROUP0_DRIVE:
+ *value = ftdi->eeprom->group0_drive;
+ break;
+ case GROUP0_SCHMITT:
+ *value = ftdi->eeprom->group0_schmitt;
+ break;
+ case GROUP0_SLEW:
+ *value = ftdi->eeprom->group0_slew;
+ break;
+ case GROUP1_DRIVE:
+ *value = ftdi->eeprom->group1_drive;
+ break;
+ case GROUP1_SCHMITT:
+ *value = ftdi->eeprom->group1_schmitt;
+ break;
+ case GROUP1_SLEW:
+ *value = ftdi->eeprom->group1_slew;
+ break;
+ case GROUP2_DRIVE:
+ *value = ftdi->eeprom->group2_drive;
+ break;
+ case GROUP2_SCHMITT:
+ *value = ftdi->eeprom->group2_schmitt;
+ break;
+ case GROUP2_SLEW:
+ *value = ftdi->eeprom->group2_slew;
+ break;
+ case GROUP3_DRIVE:
+ *value = ftdi->eeprom->group3_drive;
+ break;
+ case GROUP3_SCHMITT:
+ *value = ftdi->eeprom->group3_schmitt;
+ break;
+ case GROUP3_SLEW:
+ *value = ftdi->eeprom->group3_slew;
+ break;
+ case CHIP_TYPE:
+ *value = ftdi->eeprom->chip;
+ break;
+ case CHIP_SIZE:
+ *value = ftdi->eeprom->size;
+ break;
+ default:
+ ftdi_error_return(-1, "Request for unknown EEPROM value");
+ }
+ return 0;
+}
+
+/**
+ Set a value in the decoded EEPROM Structure
+ No parameter checking is performed
+
+ \\param ftdi pointer to ftdi_context
+ \\param value_name Enum of the value to query
+ \\param Value to set
+
+ \\retval 0: all fine
+ \\retval -1: Value doesn't exist
+ \\retval -2: Value not user settable
+*/
+int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int value)
+{
+ switch (value_name)
+ {
+ case VENDOR_ID:
+ ftdi->eeprom->vendor_id = value;
+ break;
+ case PRODUCT_ID:
+ ftdi->eeprom->product_id = value;
+ break;
+ case SELF_POWERED:
+ ftdi->eeprom->self_powered = value;
+ break;
+ case REMOTE_WAKEUP:
+ ftdi->eeprom->remote_wakeup = value;
+ break;
+ case IS_NOT_PNP:
+ ftdi->eeprom->is_not_pnp = value;
+ break;
+ case SUSPEND_DBUS7:
+ ftdi->eeprom->suspend_dbus7 = value;
+ break;
+ case IN_IS_ISOCHRONOUS:
+ ftdi->eeprom->in_is_isochronous = value;
+ break;
+ case SUSPEND_PULL_DOWNS:
+ ftdi->eeprom->suspend_pull_downs = value;
+ break;
+ case USE_SERIAL:
+ ftdi->eeprom->use_serial = value;
+ break;
+ case USB_VERSION:
+ ftdi->eeprom->usb_version = value;
+ break;
+ case MAX_POWER:
+ ftdi->eeprom->max_power = value;
+ break;
+ case CHANNEL_A_TYPE:
+ ftdi->eeprom->channel_a_type = value;
+ break;
+ case CHANNEL_B_TYPE:
+ ftdi->eeprom->channel_b_type = value;
+ break;
+ case CHANNEL_A_DRIVER:
+ ftdi->eeprom->channel_a_driver = value;
+ break;
+ case CHANNEL_B_DRIVER:
+ ftdi->eeprom->channel_b_driver = value;
+ break;
+ case CBUS_FUNCTION_0:
+ ftdi->eeprom->cbus_function[0] = value;
+ break;
+ case CBUS_FUNCTION_1:
+ ftdi->eeprom->cbus_function[1] = value;
+ break;
+ case CBUS_FUNCTION_2:
+ ftdi->eeprom->cbus_function[2] = value;
+ break;
+ case CBUS_FUNCTION_3:
+ ftdi->eeprom->cbus_function[3] = value;
+ break;
+ case CBUS_FUNCTION_4:
+ ftdi->eeprom->cbus_function[4] = value;
+ break;
+ case HIGH_CURRENT:
+ ftdi->eeprom->high_current = value;
+ break;
+ case HIGH_CURRENT_A:
+ ftdi->eeprom->high_current_a = value;
+ break;
+ case HIGH_CURRENT_B:
+ ftdi->eeprom->high_current_b = value;
+ break;
+ case INVERT:
+ ftdi->eeprom->invert = value;
+ break;
+ case GROUP0_DRIVE:
+ ftdi->eeprom->group0_drive = value;
+ break;
+ case GROUP0_SCHMITT:
+ ftdi->eeprom->group0_schmitt = value;
+ break;
+ case GROUP0_SLEW:
+ ftdi->eeprom->group0_slew = value;
+ break;
+ case GROUP1_DRIVE:
+ ftdi->eeprom->group1_drive = value;
+ break;
+ case GROUP1_SCHMITT:
+ ftdi->eeprom->group1_schmitt = value;
+ break;
+ case GROUP1_SLEW:
+ ftdi->eeprom->group1_slew = value;
+ break;
+ case GROUP2_DRIVE:
+ ftdi->eeprom->group2_drive = value;
+ break;
+ case GROUP2_SCHMITT:
+ ftdi->eeprom->group2_schmitt = value;
+ break;
+ case GROUP2_SLEW:
+ ftdi->eeprom->group2_slew = value;
+ break;
+ case GROUP3_DRIVE:
+ ftdi->eeprom->group3_drive = value;
+ break;
+ case GROUP3_SCHMITT:
+ ftdi->eeprom->group3_schmitt = value;
+ break;
+ case GROUP3_SLEW:
+ ftdi->eeprom->group3_slew = value;
+ break;
+ case CHIP_TYPE:
+ ftdi->eeprom->chip = value;
+ break;
+ case CHIP_SIZE:
+ ftdi_error_return(-2, "EEPROM Value can't be changed");
+ default :
+ ftdi_error_return(-1, "Request to unknown EEPROM value");
+ }
+ return 0;
+}
+
+/** Get the read-only buffer to the binary EEPROM content
+
+ \param ftdi pointer to ftdi_context
+ \param ftdi buffer to receive EEPROM content
+ \param size Size of receiving buffer
+
+ \retval 0: All fine
+ \retval -1: struct ftdi_contxt or ftdi_eeprom missing
+*/
+int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size)
+ {
+ if (!ftdi || !(ftdi->eeprom))
+ ftdi_error_return(-1, "No appropriate structure");
+ memcpy(buf, ftdi->eeprom->buf, size);
+ return 0;
+ }
+
+/**
Read eeprom location
\param ftdi pointer to ftdi_context