EEPROM: treat USE_SERIAL as bool, not a flag
[libftdi] / src / ftdi.c
index 4dc47bd..612376b 100644 (file)
@@ -63,6 +63,8 @@ static void ftdi_usb_close_internal (struct ftdi_context *ftdi)
     {
         libusb_close (ftdi->usb_dev);
         ftdi->usb_dev = NULL;
+        if(ftdi->eeprom)
+            ftdi->eeprom->initialized_for_connected_device = 0;
     }
 }
 
@@ -293,8 +295,10 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli
         if (libusb_get_device_descriptor(dev, &desc) < 0)
             ftdi_error_return_free_device_list(-6, "libusb_get_device_descriptor() failed", devs);
 
-        if ((vendor != 0 && product != 0 && desc.idVendor == vendor && desc.idProduct == product) ||
-            ((desc.idVendor == 0x403) && (desc.idProduct == 0x6001 || desc.idProduct == 0x6010
+        if (((vendor != 0 && product != 0) && 
+             desc.idVendor == vendor && desc.idProduct == product) ||
+            ((vendor == 0 && product == 0) && 
+             (desc.idVendor == 0x403) && (desc.idProduct == 0x6001 || desc.idProduct == 0x6010
                                           || desc.idProduct == 0x6011 || desc.idProduct == 0x6014)))
         {
             *curdev = (struct ftdi_device_list*)malloc(sizeof(struct ftdi_device_list));
@@ -1073,7 +1077,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_2232C || ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H)
+    if (ftdi->type == TYPE_2232C || ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H || ftdi->type == TYPE_232H )
     {
         *index = (unsigned short)(encoded_divisor >> 8);
         *index &= 0xFF00;
@@ -1087,6 +1091,16 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
 }
 
 /**
+ * @brief Wrapper function to export ftdi_convert_baudrate() to the unit test
+ * Do not use, it's only for the unit test framework
+ **/
+int convert_baudrate_UT_export(int baudrate, struct ftdi_context *ftdi,
+                                 unsigned short *value, unsigned short *index)
+{
+    return ftdi_convert_baudrate(baudrate, ftdi, value, index);
+}
+
+/**
     Sets the chip baud rate
 
     \param ftdi pointer to ftdi_context
@@ -2175,7 +2189,7 @@ int ftdi_set_error_char(struct ftdi_context *ftdi,
 }
 
 /**
-    Init eeprom with default values.
+    Init eeprom with default values for the connected device
     \param ftdi pointer to ftdi_context
     \param manufacturer String to use as Manufacturer
     \param product String to use as Product description
@@ -2184,6 +2198,7 @@ int ftdi_set_error_char(struct ftdi_context *ftdi,
     \retval  0: all fine
     \retval -1: No struct ftdi_context
     \retval -2: No struct ftdi_eeprom
+    \retval -3: No connected device or device not yet opened
 */
 int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
                              char * product, char * serial)
@@ -2199,8 +2214,11 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
     eeprom = ftdi->eeprom;
     memset(eeprom, 0, sizeof(struct ftdi_eeprom));
 
+    if (ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "No connected device or device not yet opened");
+
     eeprom->vendor_id = 0x0403;
-    eeprom->use_serial = USE_SERIAL_NUM;
+    eeprom->use_serial = 1;
     if ((ftdi->type == TYPE_AM) || (ftdi->type == TYPE_BM) ||
             (ftdi->type == TYPE_R))
         eeprom->product_id = 0x6001;
@@ -2267,6 +2285,7 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
         }
         eeprom->size = -1;
     }
+    eeprom->initialized_for_connected_device = 1;
     return 0;
 }
 /*FTD2XX doesn't check for values not fitting in the ACBUS Signal oprtions*/
@@ -2288,6 +2307,42 @@ void set_ft232h_cbus(struct ftdi_eeprom *eeprom, unsigned char * output)
         output[0x18+i] = mode_high <<4 | mode_low;
     }
 }
+/* Return the bits for the encoded EEPROM Structure of a requested Mode
+ *
+ */
+static unsigned char type2bit(unsigned char type, enum ftdi_chip_type chip)
+{
+    switch (chip)
+    {
+    case TYPE_2232H:
+    case TYPE_2232C:
+    {
+        switch (type)
+        {
+        case CHANNEL_IS_UART: return 0;
+        case CHANNEL_IS_FIFO: return 0x01;
+        case CHANNEL_IS_OPTO: return 0x02;
+        case CHANNEL_IS_CPU : return 0x04;
+        default: return 0;
+        }
+    }
+    case TYPE_232H:
+    {
+        switch (type)
+        {
+        case CHANNEL_IS_UART   : return 0;
+        case CHANNEL_IS_FIFO   : return 0x01;
+        case CHANNEL_IS_OPTO   : return 0x02;
+        case CHANNEL_IS_CPU    : return 0x04;
+        case CHANNEL_IS_FT1284 : return 0x08;
+        default: return 0;
+        }
+    }
+    default: return 0;
+    }
+    return 0;
+}    
+
 /**
     Build binary buffer from ftdi_eeprom structure.
     Output is suitable for ftdi_write_eeprom().
@@ -2351,6 +2406,9 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
         case TYPE_4232H:
             user_area_size = 86;
             break;
+        case TYPE_232H:
+            user_area_size = 80;
+            break;
         default:
             user_area_size = 0;
             break;
@@ -2511,7 +2569,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
     if (ftdi->type > TYPE_AM) /* use_serial not used in AM devices */
     {
-        if (eeprom->use_serial == USE_SERIAL_NUM )
+        if (eeprom->use_serial)
             output[0x0A] |= USE_SERIAL_NUM;
         else
             output[0x0A] &= ~USE_SERIAL_NUM;
@@ -2534,7 +2592,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             break;
         case TYPE_2232C:
 
-            output[0x00] = (eeprom->channel_a_type)?((1<<(eeprom->channel_a_type)) & 0x7):0;
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_2232C);
             if ( eeprom->channel_a_driver == DRIVER_VCP)
                 output[0x00] |= DRIVER_VCP;
             else
@@ -2545,7 +2603,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             else
                 output[0x00] &= ~HIGH_CURRENT_DRIVE;
 
-            output[0x01] = (eeprom->channel_b_type)?((1<<(eeprom->channel_b_type)) & 0x7):0;
+            output[0x01] = type2bit(eeprom->channel_b_type, TYPE_2232C);
             if ( eeprom->channel_b_driver == DRIVER_VCP)
                 output[0x01] |= DRIVER_VCP;
             else
@@ -2616,13 +2674,13 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
                 output[0x16] = eeprom->cbus_function[4];
             break;
         case TYPE_2232H:
-            output[0x00] = (eeprom->channel_a_type)?((1<<(eeprom->channel_a_type)) & 0x7):0;
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_2232H);
             if ( eeprom->channel_a_driver == DRIVER_VCP)
                 output[0x00] |= DRIVER_VCP;
             else
                 output[0x00] &= ~DRIVER_VCP;
 
-            output[0x01] = (eeprom->channel_b_type)?((1<<(eeprom->channel_b_type)) & 0x7):0;
+            output[0x01] = type2bit(eeprom->channel_b_type, TYPE_2232H);
             if ( eeprom->channel_b_driver == DRIVER_VCP)
                 output[0x01] |= DRIVER_VCP;
             else
@@ -2681,7 +2739,7 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             fprintf(stderr,"FIXME: Build FT4232H specific EEPROM settings\n");
             break;
         case TYPE_232H:
-            output[0x00] = (eeprom->channel_a_type)?((1<<(eeprom->channel_a_type)) & 0xf):0;
+            output[0x00] = type2bit(eeprom->channel_a_type, TYPE_232H);
             if ( eeprom->channel_a_driver == DRIVER_VCP)
                 output[0x00] |= DRIVER_VCPH;
             else
@@ -2745,23 +2803,26 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
 
     return user_area_size;
 }
-/* FTD2XX doesn't allow to set multiple bits in the interface mode bitfield*/
-unsigned char bit2type(unsigned char bits)
+/* Decode the encoded EEPROM field for the FTDI Mode into a value for the abstracted 
+ * EEPROM structure
+ *
+ * FTD2XX doesn't allow to set multiple bits in the interface mode bitfield, and so do we
+ */
+static unsigned char bit2type(unsigned char bits)
 {
     switch (bits)
     {
-    case 0: return 0;
-    case 1: return 1;
-    case 2: return 2;
-    case 4: return 3;
-    case 8: return 4;
+    case   0: return CHANNEL_IS_UART;
+    case   1: return CHANNEL_IS_FIFO;
+    case   2: return CHANNEL_IS_OPTO;
+    case   4: return CHANNEL_IS_CPU;
+    case   8: return CHANNEL_IS_FT1284;
     default:
         fprintf(stderr," Unexpected value %d for Hardware Interface type\n",
                 bits);
     }
     return 0;
 }
-
 /**
    Decode binary EEPROM image into an ftdi_eeprom structure.
 
@@ -2823,7 +2884,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     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->use_serial         = (buf[0x0A] & USE_SERIAL_NUM)?1:0;
     eeprom->use_usb_version    = buf[0x0A] & USE_USB_VERSION_BIT;
 
     // Addr 0C: USB version low byte when 0x0A
@@ -2957,7 +3018,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
     {
         eeprom->channel_a_type   = bit2type(buf[0x00] & 0x7);
         eeprom->channel_a_driver = buf[0x00] & DRIVER_VCP;
-        eeprom->channel_b_type   = buf[0x01] & 0x7;
+        eeprom->channel_b_type   = bit2type(buf[0x01] & 0x7);
         eeprom->channel_b_driver = buf[0x01] & DRIVER_VCP;
 
         if (ftdi->type == TYPE_2232H)
@@ -3005,7 +3066,7 @@ int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose)
 
     if (verbose)
     {
-        char *channel_mode[] = {"UART","245","CPU", "OPTO", "FT1284"};
+        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);
@@ -3179,6 +3240,9 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case IN_IS_ISOCHRONOUS:
             *value = ftdi->eeprom->in_is_isochronous;
             break;
+        case OUT_IS_ISOCHRONOUS:
+            *value = ftdi->eeprom->out_is_isochronous;
+            break;
         case SUSPEND_PULL_DOWNS:
             *value = ftdi->eeprom->suspend_pull_downs;
             break;
@@ -3188,6 +3252,9 @@ int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case USB_VERSION:
             *value = ftdi->eeprom->usb_version;
             break;
+        case USE_USB_VERSION:
+            *value = ftdi->eeprom->use_usb_version;
+            break;
         case MAX_POWER:
             *value = ftdi->eeprom->max_power;
             break;
@@ -3342,6 +3409,9 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case IN_IS_ISOCHRONOUS:
             ftdi->eeprom->in_is_isochronous = value;
             break;
+        case OUT_IS_ISOCHRONOUS:
+            ftdi->eeprom->out_is_isochronous = value;
+            break;
         case SUSPEND_PULL_DOWNS:
             ftdi->eeprom->suspend_pull_downs = value;
             break;
@@ -3351,6 +3421,9 @@ int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value valu
         case USB_VERSION:
             ftdi->eeprom->usb_version = value;
             break;
+        case USE_USB_VERSION:
+            ftdi->eeprom->use_usb_version = value;
+            break;
         case MAX_POWER:
             ftdi->eeprom->max_power = value;
             break;
@@ -3494,6 +3567,29 @@ int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size
     return 0;
 }
 
+/** Set the EEPROM content from the user-supplied prefilled buffer
+
+    \param ftdi pointer to ftdi_context
+    \param buf buffer to read EEPROM content
+    \param size Size of buffer
+
+    \retval 0: All fine
+    \retval -1: struct ftdi_contxt or ftdi_eeprom of buf missing
+*/
+int ftdi_set_eeprom_buf(struct ftdi_context *ftdi, const unsigned char * buf, int size)
+{
+    if (!ftdi || !(ftdi->eeprom) || !buf)
+        ftdi_error_return(-1, "No appropriate structure");
+
+    // Only copy up to FTDI_MAX_EEPROM_SIZE bytes
+    if (size > FTDI_MAX_EEPROM_SIZE)
+        size = FTDI_MAX_EEPROM_SIZE;
+
+    memcpy(ftdi->eeprom->buf, buf, size);
+
+    return 0;
+}
+
 /**
     Read eeprom location
 
@@ -3676,6 +3772,7 @@ int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr,
     \retval  0: all fine
     \retval -1: read failed
     \retval -2: USB device unavailable
+    \retval -3: EEPROM not initialized for the connected device;
 */
 int ftdi_write_eeprom(struct ftdi_context *ftdi)
 {
@@ -3685,6 +3782,10 @@ int ftdi_write_eeprom(struct ftdi_context *ftdi)
 
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-2, "USB device unavailable");
+
+    if(ftdi->eeprom->initialized_for_connected_device == 0)
+        ftdi_error_return(-3, "EEPROM not initialized for the connected device");
+
     eeprom = ftdi->eeprom->buf;
 
     /* These commands were traced while running MProg */