eeprom: Add channel_a_driver support for type xxR chips
[libftdi] / src / ftdi.c
index 2fff1ff..7234229 100644 (file)
@@ -4,6 +4,7 @@
     begin                : Fri Apr 4 2003
     copyright            : (C) 2003-2017 by Intra2net AG and the libftdi developers
     email                : opensource@intra2net.com
+    SPDX-License-Identifier: LGPL-2.1-only
  ***************************************************************************/
 
 /***************************************************************************
@@ -35,6 +36,8 @@
 #include <stdlib.h>
 
 #include "ftdi_i.h"
+/* Prevent deprecated messages when building library */
+#define _FTDI_DISABLE_DEPRECATED
 #include "ftdi.h"
 #include "ftdi_version_i.h"
 
@@ -87,7 +90,7 @@ static void ftdi_usb_close_internal (struct ftdi_context *ftdi)
 */
 int ftdi_init(struct ftdi_context *ftdi)
 {
-    struct ftdi_eeprom* eeprom = (struct ftdi_eeprom *)malloc(sizeof(struct ftdi_eeprom));
+    struct ftdi_eeprom* eeprom;
     ftdi->usb_ctx = NULL;
     ftdi->usb_dev = NULL;
     ftdi->usb_read_timeout = 5000;
@@ -111,6 +114,7 @@ int ftdi_init(struct ftdi_context *ftdi)
     ftdi_set_interface(ftdi, INTERFACE_ANY);
     ftdi->bitbang_mode = 1; /* when bitbang is enabled this holds the number of the mode  */
 
+    eeprom = (struct ftdi_eeprom *)malloc(sizeof(struct ftdi_eeprom));
     if (eeprom == 0)
         ftdi_error_return(-2, "Can't malloc struct ftdi_eeprom");
     memset(eeprom, 0, sizeof(struct ftdi_eeprom));
@@ -613,6 +617,11 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
         if (libusb_detach_kernel_driver(ftdi->usb_dev, ftdi->interface) !=0)
             detach_errno = errno;
     }
+    else if (ftdi->module_detach_mode == AUTO_DETACH_REATACH_SIO_MODULE)
+    {
+        if (libusb_set_auto_detach_kernel_driver(ftdi->usb_dev, 1) != LIBUSB_SUCCESS)
+            detach_errno = errno;
+    }
 
     if (libusb_get_configuration (ftdi->usb_dev, &cfg) < 0)
         ftdi_error_return(-12, "libusb_get_configuration () failed");
@@ -750,6 +759,7 @@ int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
     \retval -9: get serial number failed
     \retval -10: unable to close device
     \retval -11: ftdi context invalid
+    \retval -12: libusb_get_device_list() failed
 */
 int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
                              const char* description, const char* serial, unsigned int index)
@@ -824,6 +834,54 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
 }
 
 /**
+    Opens the device at a given USB bus and device address.
+
+    \param ftdi pointer to ftdi_context
+    \param bus Bus number
+    \param addr Device address
+
+    \retval  0: all fine
+    \retval -1: usb_find_busses() failed
+    \retval -2: usb_find_devices() failed
+    \retval -3: usb device not found
+    \retval -4: unable to open device
+    \retval -5: unable to claim device
+    \retval -6: reset failed
+    \retval -7: set baudrate failed
+    \retval -8: get product description failed
+    \retval -9: get serial number failed
+    \retval -10: unable to close device
+    \retval -11: ftdi context invalid
+    \retval -12: libusb_get_device_list() failed
+*/
+int ftdi_usb_open_bus_addr(struct ftdi_context *ftdi, uint8_t bus, uint8_t addr)
+{
+    libusb_device *dev;
+    libusb_device **devs;
+    int i = 0;
+
+    if (ftdi == NULL)
+        ftdi_error_return(-11, "ftdi context invalid");
+
+    if (libusb_get_device_list(ftdi->usb_ctx, &devs) < 0)
+        ftdi_error_return(-12, "libusb_get_device_list() failed");
+
+    while ((dev = devs[i++]) != NULL)
+    {
+        if (libusb_get_bus_number(dev) == bus && libusb_get_device_address(dev) == addr)
+        {
+            int res;
+            res = ftdi_usb_open_dev(ftdi, dev);
+            libusb_free_device_list(devs,1);
+            return res;
+        }
+    }
+
+    // device not found
+    ftdi_error_return_free_device_list(-3, "device not found", devs);
+}
+
+/**
     Opens the ftdi-device described by a description-string.
     Intended to be used for parsing a device-description given as commandline argument.
 
@@ -961,6 +1019,7 @@ int ftdi_usb_reset(struct ftdi_context *ftdi)
 
 /**
     Clears the read buffer on the chip and the internal read buffer.
+    This is the correct behavior for an RX flush.
 
     \param ftdi pointer to ftdi_context
 
@@ -968,6 +1027,36 @@ int ftdi_usb_reset(struct ftdi_context *ftdi)
     \retval -1: read buffer purge failed
     \retval -2: USB device unavailable
 */
+int ftdi_tciflush(struct ftdi_context *ftdi)
+{
+    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_RESET_REQUEST, SIO_TCIFLUSH,
+                                ftdi->index, NULL, 0, ftdi->usb_write_timeout) < 0)
+        ftdi_error_return(-1, "FTDI purge of RX buffer failed");
+
+    // Invalidate data in the readbuffer
+    ftdi->readbuffer_offset = 0;
+    ftdi->readbuffer_remaining = 0;
+
+    return 0;
+}
+
+
+/**
+    Clears the write buffer on the chip and the internal read buffer.
+    This is incorrect behavior for an RX flush.
+
+    \param ftdi pointer to ftdi_context
+
+    \retval  0: all fine
+    \retval -1: write buffer purge failed
+    \retval -2: USB device unavailable
+
+    \deprecated Use \ref ftdi_tciflush(struct ftdi_context *ftdi)
+*/
 int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
 {
     if (ftdi == NULL || ftdi->usb_dev == NULL)
@@ -987,6 +1076,7 @@ int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
 
 /**
     Clears the write buffer on the chip.
+    This is correct behavior for a TX flush.
 
     \param ftdi pointer to ftdi_context
 
@@ -994,6 +1084,32 @@ int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
     \retval -1: write buffer purge failed
     \retval -2: USB device unavailable
 */
+int ftdi_tcoflush(struct ftdi_context *ftdi)
+{
+    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_RESET_REQUEST, SIO_TCOFLUSH,
+                                ftdi->index, NULL, 0, ftdi->usb_write_timeout) < 0)
+        ftdi_error_return(-1, "FTDI purge of TX buffer failed");
+
+    return 0;
+}
+
+
+/**
+    Clears the read buffer on the chip.
+    This is incorrect behavior for a TX flush.
+
+    \param ftdi pointer to ftdi_context
+
+    \retval  0: all fine
+    \retval -1: read buffer purge failed
+    \retval -2: USB device unavailable
+
+    \deprecated Use \ref ftdi_tcoflush(struct ftdi_context *ftdi)
+*/
 int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
 {
     if (ftdi == NULL || ftdi->usb_dev == NULL)
@@ -1008,7 +1124,37 @@ int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
 }
 
 /**
+    Clears the RX and TX FIFOs on the chip and the internal read buffer.
+    This is correct behavior for both RX and TX flush.
+
+    \param ftdi pointer to ftdi_context
+
+    \retval  0: all fine
+    \retval -1: read buffer purge failed
+    \retval -2: write buffer purge failed
+    \retval -3: USB device unavailable
+*/
+int ftdi_tcioflush(struct ftdi_context *ftdi)
+{
+    int result;
+
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "USB device unavailable");
+
+    result = ftdi_tcoflush(ftdi);
+    if (result < 0)
+        return -1;
+
+    result = ftdi_tciflush(ftdi);
+    if (result < 0)
+        return -2;
+
+    return 0;
+}
+
+/**
     Clears the buffers on the chip and the internal read buffer.
+    While coded incorrectly, the result is satisfactory.
 
     \param ftdi pointer to ftdi_context
 
@@ -1016,6 +1162,8 @@ int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
     \retval -1: read buffer purge failed
     \retval -2: write buffer purge failed
     \retval -3: USB device unavailable
+
+    \deprecated Use \ref ftdi_tcioflush(struct ftdi_context *ftdi)
 */
 int ftdi_usb_purge_buffers(struct ftdi_context *ftdi)
 {
@@ -1844,7 +1992,7 @@ int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunk
 /**
     Reads data in chunks (see ftdi_read_data_set_chunksize()) from the chip.
 
-    Automatically strips the two modem status bytes transfered during every read.
+    Automatically strips the two modem status bytes transferred during every read.
 
     \param ftdi pointer to ftdi_context
     \param buf Buffer to store data in
@@ -1859,13 +2007,14 @@ int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunk
 int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
 {
     int offset = 0, ret, i, num_of_chunks, chunk_remains;
-    int packet_size = ftdi->max_packet_size;
+    int packet_size;
     int actual_length = 1;
 
     if (ftdi == NULL || ftdi->usb_dev == NULL)
         ftdi_error_return(-666, "USB device unavailable");
 
     // Packet size sanity check (avoid division by zero)
+    packet_size = ftdi->max_packet_size;
     if (packet_size == 0)
         ftdi_error_return(-1, "max_packet_size is bogus (zero)");
 
@@ -2213,9 +2362,11 @@ int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status)
 /**
     Set flowcontrol for ftdi chip
 
+    Note: Do not use this function to enable XON/XOFF mode, use ftdi_setflowctrl_xonxoff() instead.
+
     \param ftdi pointer to ftdi_context
     \param flowctrl flow control to use. should be
-           SIO_DISABLE_FLOW_CTRL, SIO_RTS_CTS_HS, SIO_DTR_DSR_HS or SIO_XON_XOFF_HS
+           SIO_DISABLE_FLOW_CTRL, SIO_RTS_CTS_HS, SIO_DTR_DSR_HS
 
     \retval  0: all fine
     \retval -1: set flow control failed
@@ -2235,6 +2386,31 @@ int ftdi_setflowctrl(struct ftdi_context *ftdi, int flowctrl)
 }
 
 /**
+    Set XON/XOFF flowcontrol for ftdi chip
+
+    \param ftdi pointer to ftdi_context
+    \param xon character code used to resume transmission
+    \param xoff character code used to pause transmission
+
+    \retval  0: all fine
+    \retval -1: set flow control failed
+    \retval -2: USB device unavailable
+*/
+int ftdi_setflowctrl_xonxoff(struct ftdi_context *ftdi, unsigned char xon, unsigned char xoff)
+{
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
+    uint16_t xonxoff = xon | (xoff << 8);
+    if (libusb_control_transfer(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
+                                SIO_SET_FLOW_CTRL_REQUEST, xonxoff, (SIO_XON_XOFF_HS | ftdi->index),
+                                NULL, 0, ftdi->usb_write_timeout) < 0)
+        ftdi_error_return(-1, "set flow control failed");
+
+    return 0;
+}
+
+/**
     Set dtr line
 
     \param ftdi pointer to ftdi_context
@@ -2548,8 +2724,8 @@ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer,
     return 0;
 }
 
-int ftdi_eeprom_set_strings(struct ftdi_context *ftdi, char * manufacturer,
-                            char * product, char * serial)
+int ftdi_eeprom_set_strings(struct ftdi_context *ftdi, const char * manufacturer,
+                            const char * product, const char * serial)
 {
     struct ftdi_eeprom *eeprom;
 
@@ -3006,6 +3182,8 @@ int ftdi_eeprom_build(struct ftdi_context *ftdi)
             output[0x00] = type2bit(eeprom->channel_a_type, TYPE_R);
             if (eeprom->high_current == HIGH_CURRENT_DRIVE_R)
                 output[0x00] |= HIGH_CURRENT_DRIVE_R;
+            if (eeprom->channel_a_driver == DRIVER_VCP)
+                output[0x00] |= DRIVER_VCP;
             if (eeprom->external_oscillator)
                 output[0x00] |= 0x02;
             output[0x01] = 0x40; /* Hard coded Endpoint Size*/