• FTDI Driver


    /***************************************************************************
     ftdi.c  -  description
     -------------------
     begin                : Fri Apr 4 2003
     copyright            : (C) 2003 by Intra2net AG
     email                : opensource@intra2net.com
    
     Modified             : April, 2007
     : Craig Van Degrift
     : craig@yosemitefoothills.com
    
     ***************************************************************************/
    
    /***************************************************************************
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU Lesser General Public License           *
     *   version 2.1 as published by the Free Software Foundation;             *
     *                                                                         *
     ***************************************************************************/
    
    /**
     \mainpage libftdi API documentation
    
     Library to talk to FTDI chips. You find the latest versions of libftdi at
     http://www.intra2net.com/en/developer/libftdi/
    
     The library is easy to use. Have a look at this short example:
     \include simple.c
    
     More examples can be found in the "examples" directory.
     */
    /** \addtogroup libftdi */
    /* @{ */
    
    #include <usb.h>
    #include <string.h>
    #include <errno.h>
    
    #include "ftdi.h"
    
    #define ftdi_error_return(code, str) do {  \
            ftdi->error_str = str;             \
            return code;                       \
       } while(0);
    
    /**
     Initializes a ftdi_context.
    
     \param ftdi pointer to ftdi_context
    
     \retval  0: all fine
     \retval -1: couldn't allocate read buffer
    
     \remark This should be called before all functions
     */
    int ftdi_init( struct ftdi_context *ftdi )
    {
      ftdi->usb_dev = NULL;
      ftdi->usb_read_timeout = 5000;
      ftdi->usb_write_timeout = 5000;
    
      ftdi->type = TYPE_BM; /* chip type */
      ftdi->baudrate = -1;
      ftdi->bitbang_enabled = 0;
    
      ftdi->readbuffer = NULL;
      ftdi->readbuffer_offset = 0;
      ftdi->readbuffer_remaining = 0;
      ftdi->writebuffer_chunksize = 4096;
    
      ftdi->interface = 0;
      ftdi->index = 0;
      ftdi->in_ep = 0x02;
      ftdi->out_ep = 0x81;
      ftdi->bitbang_mode = 1; /* 1: Normal bitbang mode, 2: SPI bitbang mode */
    
      ftdi->error_str = NULL;
    
      /* All fine. Now allocate the readbuffer */
      return ftdi_read_data_set_chunksize( ftdi, 4096 );
    }
    
    /**
     Open selected channels on a chip, otherwise use first channel.
    
     \param ftdi pointer to ftdi_context
     \param interface Interface to use for FT2232C chips.
    
     \retval  0: all fine
     \retval -1: unknown interface
     */
    int ftdi_set_interface( struct ftdi_context *ftdi,
      enum ftdi_interface interface )
    {
      switch ( interface )
      {
        case INTERFACE_ANY:
        case INTERFACE_A:
          /* ftdi_usb_open_desc cares to set the right index, depending on the found chip */
          break;
        case INTERFACE_B:
          ftdi->interface = 1;
          ftdi->index = INTERFACE_B;
          ftdi->in_ep = 0x04;
          ftdi->out_ep = 0x83;
          break;
        default:
          ftdi_error_return( -1, "Unknown interface" )
          ;
      }
      return 0;
    }
    
    /**
     Deinitializes a ftdi_context.
    
     \param ftdi pointer to ftdi_context
     */
    void ftdi_deinit( struct ftdi_context *ftdi )
    {
      if ( ftdi->readbuffer != NULL )
      {
        free( ftdi->readbuffer );
        ftdi->readbuffer = NULL;
      }
    }
    
    /**
     Use an already open libusb device.
    
     \param ftdi pointer to ftdi_context
     \param usb libusb usb_dev_handle to use
     */
    void ftdi_set_usbdev( struct ftdi_context *ftdi, usb_dev_handle *usb )
    {
      ftdi->usb_dev = usb;
    }
    
    /**
     Finds all ftdi devices on the usb bus. Creates a new ftdi_device_list which
     needs to be deallocated by ftdi_list_free() after use.
    
     \param ftdi pointer to ftdi_context
     \param devlist Pointer where to store list of found devices
     \param vendor Vendor ID to search for
     \param product Product ID to search for
    
     \retval >0: number of devices found
     \retval -1: usb_find_busses() failed
     \retval -2: usb_find_devices() failed
     \retval -3: out of memory
     */
    int ftdi_usb_find_all( struct ftdi_context *ftdi,
      struct ftdi_device_list **devlist, int vendor, int product )
    {
      struct ftdi_device_list **curdev;
      struct usb_bus *bus;
      struct usb_device *dev;
      int count = 0;
    
      usb_init( );
      if ( usb_find_busses( ) < 0 )
        ftdi_error_return( -1, "usb_find_busses() failed" );
      if ( usb_find_devices( ) < 0 )
        ftdi_error_return( -2, "usb_find_devices() failed" );
    
      curdev = devlist;
      *curdev = NULL;
      for ( bus = usb_busses; bus; bus = bus->next )
      {
        for ( dev = bus->devices; dev; dev = dev->next )
        {
          if ( dev->descriptor.idVendor == vendor
            && dev->descriptor.idProduct == product )
          {
            *curdev = (struct ftdi_device_list*) malloc(
              sizeof(struct ftdi_device_list) );
            if ( ! *curdev )
              ftdi_error_return( -3, "out of memory" );
    
            ( *curdev )->next = NULL;
            ( *curdev )->dev = dev;
    
            curdev = &( *curdev )->next;
            count++;
          }
        }
      }
    
      return count;
    }
    
    /**
     Frees a usb device list.
    
     \param devlist USB device list created by ftdi_usb_find_all()
     */
    void ftdi_list_free( struct ftdi_device_list **devlist )
    {
      struct ftdi_device_list *curdev, *next;
    
      for ( curdev = *devlist; curdev != NULL; )
      {
        next = curdev->next;
        free( curdev );
        curdev = next;
      }
    
      *devlist = NULL;
    }
    
    /**
     Return device ID strings from the usb device.
    
     The parameters manufacturer, description and serial may be NULL
     or pointer to buffers to store the fetched strings.
    
     \note Use this function only in combination with ftdi_usb_find_all()
     as it closes the internal "usb_dev" after use.
    
     \param ftdi pointer to ftdi_context
     \param dev libusb usb_dev to use
     \param manufacturer Store manufacturer string here if not NULL
     \param mnf_len Buffer size of manufacturer string
     \param description Store product description string here if not NULL
     \param desc_len Buffer size of product description string
     \param serial Store serial string here if not NULL
     \param serial_len Buffer size of serial string
    
     \retval   0: all fine
     \retval  -1: wrong arguments
     \retval  -4: unable to open device
     \retval  -7: get product manufacturer failed
     \retval  -8: get product description failed
     \retval  -9: get serial number failed
     \retval -10: unable to close device
     */
    int ftdi_usb_get_strings( struct ftdi_context * ftdi, struct usb_device * dev,
      char * manufacturer, int mnf_len, char * description, int desc_len,
      char * serial, int serial_len )
    {
      if ( ( ftdi == NULL ) || ( dev == NULL ) )
        return -1;
    
      if ( !( ftdi->usb_dev = usb_open( dev ) ) )
        ftdi_error_return( -4, usb_strerror( ) );
    
      if ( manufacturer != NULL )
      {
        if ( usb_get_string_simple( ftdi->usb_dev, dev->descriptor.iManufacturer,
          manufacturer, mnf_len ) <= 0 )
        {
          usb_close( ftdi->usb_dev );
          ftdi_error_return( -7, usb_strerror( ) );
        }
      }
    
      if ( description != NULL )
      {
        if ( usb_get_string_simple( ftdi->usb_dev, dev->descriptor.iProduct,
          description, desc_len ) <= 0 )
        {
          usb_close( ftdi->usb_dev );
          ftdi_error_return( -8, usb_strerror( ) );
        }
      }
    
      if ( serial != NULL )
      {
        if ( usb_get_string_simple( ftdi->usb_dev, dev->descriptor.iSerialNumber,
          serial, serial_len ) <= 0 )
        {
          usb_close( ftdi->usb_dev );
          ftdi_error_return( -9, usb_strerror( ) );
        }
      }
    
      if ( usb_close( ftdi->usb_dev ) != 0 )
        ftdi_error_return( -10, usb_strerror( ) );
    
      return 0;
    }
    
    /**
     Opens a ftdi device given by a usb_device.
    
     \param ftdi pointer to ftdi_context
     \param dev libusb usb_dev to use
    
     \retval  0: all fine
     \retval -4: unable to open device
     \retval -5: unable to claim device
     \retval -6: reset failed
     \retval -7: set baudrate failed
     */
    int ftdi_usb_open_dev( struct ftdi_context *ftdi, struct usb_device *dev )
    {
      int detach_errno = 0;
      if ( !( ftdi->usb_dev = usb_open( dev ) ) )
        ftdi_error_return( -4, "usb_open() failed" );
    
    #ifdef LIBUSB_HAS_GET_DRIVER_NP
      // Try to detach ftdi_sio kernel module
      // Returns ENODATA if driver is not loaded
      if (usb_detach_kernel_driver_np(ftdi->usb_dev, ftdi->interface) != 0 && errno != ENODATA)
      detach_errno = errno;
    #endif
    
      if ( usb_claim_interface( ftdi->usb_dev, ftdi->interface ) != 0 )
      {
        usb_close( ftdi->usb_dev );
        if ( detach_errno == EPERM )
        {
          ftdi_error_return( -8, "inappropriate permissions on device!" );
        }
        else
        {
          ftdi_error_return( -5,
            "unable to claim usb device. Make sure ftdi_sio is unloaded!" );
        }
      }
    
      if ( ftdi_usb_reset( ftdi ) != 0 )
      {
        usb_close( ftdi->usb_dev );
        ftdi_error_return( -6, "ftdi_usb_reset failed" );
      }
    
      if ( ftdi_set_baudrate( ftdi, 9600 ) != 0 )
      {
        usb_close( ftdi->usb_dev );
        ftdi_error_return( -7, "set baudrate failed" );
      }
    
      // Try to guess chip type
      // Bug in the BM type chips: bcdDevice is 0x200 for serial == 0
    
      if ( dev->config->bNumInterfaces == 2 )
      {
        if ( dev->descriptor.bcdDevice < 0x500 )
          ftdi_error_return( -8,
            "has 2 interfaces so looks like FT2232 device, but bcdDevice value is less than 0x500" );
        ftdi->type = TYPE_2232C;
        if ( !ftdi->index )
          ftdi->index = INTERFACE_A;
      }
      else if ( dev->descriptor.bcdDevice < 0x200 )
        ftdi->type = TYPE_OLD;
      else if ( dev->descriptor.bcdDevice < 0x400
        && dev->descriptor.iSerialNumber != 0 )
        ftdi->type = TYPE_AM;
      else if ( dev->descriptor.bcdDevice < 0x500 )
        ftdi->type = TYPE_BM;
      else
        ftdi_error_return( -9,
          "has only 1 interface, but dcdDevice value >= 0x500" );
    
      ftdi_error_return( 0, "all fine" );
    }
    
    /**
     Opens the first device with a given vendor and product ids.
    
     \param ftdi pointer to ftdi_context
     \param vendor Vendor ID
     \param product Product ID
    
     \retval same as ftdi_usb_open_desc()
     */
    int ftdi_usb_open( struct ftdi_context *ftdi, int vendor, int product )
    {
      return ftdi_usb_open_desc( ftdi, vendor, product, NULL, NULL );
    }
    
    /**
     Opens the first device with a given, vendor id, product id,
     description and serial.
    
     \param ftdi pointer to ftdi_context
     \param vendor Vendor ID
     \param product Product ID
     \param description Description to search for. Use NULL if not needed.
     \param serial Serial to search for. Use NULL if not needed.
    
     \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
     */
    int ftdi_usb_open_desc( struct ftdi_context *ftdi, int vendor, int product,
      const char* description, const char* serial )
    {
      struct usb_bus *bus;
      struct usb_device *dev;
      char string[ 256 ];
    
      usb_init( );
    
      if ( usb_find_busses( ) < 0 )
        ftdi_error_return( -1, "usb_find_busses() failed" );
      if ( usb_find_devices( ) < 0 )
        ftdi_error_return( -2, "usb_find_devices() failed" );
    
      for ( bus = usb_busses; bus; bus = bus->next )
      {
        for ( dev = bus->devices; dev; dev = dev->next )
        {
          if ( dev->descriptor.idVendor == vendor
            && dev->descriptor.idProduct == product )
          {
            if ( !( ftdi->usb_dev = usb_open( dev ) ) )
              ftdi_error_return( -4, "usb_open() failed" );
    
            if ( description != NULL )
            {
              if ( usb_get_string_simple( ftdi->usb_dev, dev->descriptor.iProduct,
                string, sizeof( string ) ) <= 0 )
              {
                usb_close( ftdi->usb_dev );
                ftdi_error_return( -8, "unable to fetch product description" );
              }
              if ( strncmp( string, description, sizeof( string ) ) != 0 )
              {
                if ( usb_close( ftdi->usb_dev ) != 0 )
                  ftdi_error_return( -10, "unable to close device" );
                continue;
              }
            }
            if ( serial != NULL )
            {
              if ( usb_get_string_simple( ftdi->usb_dev,
                dev->descriptor.iSerialNumber, string, sizeof( string ) ) <= 0 )
              {
                usb_close( ftdi->usb_dev );
                ftdi_error_return( -9, "unable to fetch serial number" );
              }
              if ( strncmp( string, serial, sizeof( string ) ) != 0 )
              {
                if ( usb_close( ftdi->usb_dev ) != 0 )
                  ftdi_error_return( -10, "unable to close device" );
                continue;
              }
            }
    
            if ( usb_close( ftdi->usb_dev ) != 0 )
              ftdi_error_return( -10, "unable to close device" );
    
            return ftdi_usb_open_dev( ftdi, dev );
          }
        }
      }
    
      // device not found
      ftdi_error_return( -3, "device not found" );
    }
    
    /**
     Resets the ftdi device.
    
     \param ftdi pointer to ftdi_context
    
     \retval  0: all fine
     \retval -1: FTDI reset failed
     */
    int ftdi_usb_reset( struct ftdi_context *ftdi )
    {
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0, 0, ftdi->index, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1, "FTDI reset failed" );
    
      // Invalidate data in the readbuffer
      ftdi->readbuffer_offset = 0;
      ftdi->readbuffer_remaining = 0;
    
      return 0;
    }
    
    /**
     Clears the buffers on the chip.
    
     \param ftdi pointer to ftdi_context
    
     \retval  0: all fine
     \retval -1: write buffer purge failed
     \retval -2: read buffer purge failed
     */
    int ftdi_usb_purge_buffers( struct ftdi_context *ftdi )
    {
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0, 1, 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;
    
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0, 2, ftdi->index, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -2, "FTDI purge of TX buffer failed" );
    
      return 0;
    }
    
    /**
     Closes the ftdi device. Call ftdi_deinit() if you're cleaning up.
    
     \param ftdi pointer to ftdi_context
    
     \retval  0: all fine
     \retval -1: usb_release failed
     \retval -2: usb_close failed
     */
    int ftdi_usb_close( struct ftdi_context *ftdi )
    {
      int rtn = 0;
    
      if ( usb_release_interface( ftdi->usb_dev, ftdi->interface ) != 0 )
        rtn = -1;
    
      if ( usb_close( ftdi->usb_dev ) != 0 )
        rtn = -2;
    
      return rtn;
    }
    
    /*
     ftdi_convert_baudrate returns nearest supported baud rate to that requested.
     Function is only used internally
     \internal
     */
    static int ftdi_convert_baudrate( int baudrate, struct ftdi_context *ftdi,
      unsigned short *value, unsigned short *index )
    {
      static const char am_adjust_up[ 8 ] =
      {
        0,
        0,
        0,
        1,
        0,
        3,
        2,
        1 };
      static const char am_adjust_dn[ 8 ] =
      {
        0,
        0,
        0,
        1,
        0,
        1,
        2,
        3 };
      static const char frac_code[ 8 ] =
      {
        0,
        3,
        2,
        4,
        1,
        5,
        6,
        7 };
      int divisor, best_divisor, best_baud, best_baud_diff;
      unsigned long encoded_divisor;
      int i;
    
      if ( baudrate <= 0 )
      {
        // Return error
        return -1;
      }
    
      divisor = 24000000 / baudrate;
    
      if ( ftdi->type == TYPE_AM )
      {
        // Round down to supported fraction (AM only)
        divisor -= am_adjust_dn[ divisor & 7 ];
      }
    
      // Try this divisor and the one above it (because division rounds down)
      best_divisor = 0;
      best_baud = 0;
      best_baud_diff = 0;
      for ( i = 0; i < 2; i++ )
      {
        int try_divisor = divisor + i;
        int baud_estimate;
        int baud_diff;
    
        // Round up to supported divisor value
        if ( try_divisor <= 8 )
        {
          // Round up to minimum supported divisor
          try_divisor = 8;
        }
        else if ( ftdi->type != TYPE_AM && try_divisor < 12 )
        {
          // BM doesn't support divisors 9 through 11 inclusive
          try_divisor = 12;
        }
        else if ( divisor < 16 )
        {
          // AM doesn't support divisors 9 through 15 inclusive
          try_divisor = 16;
        }
        else
        {
          if ( ftdi->type == TYPE_AM )
          {
            // Round up to supported fraction (AM only)
            try_divisor += am_adjust_up[ try_divisor & 7 ];
            if ( try_divisor > 0x1FFF8 )
            {
              // Round down to maximum supported divisor value (for AM)
              try_divisor = 0x1FFF8;
            }
          }
          else
          {
            if ( try_divisor > 0x1FFFF )
            {
              // Round down to maximum supported divisor value (for BM)
              try_divisor = 0x1FFFF;
            }
          }
        }
        // Get estimated baud rate (to nearest integer)
        baud_estimate = ( 24000000 + ( try_divisor / 2 ) ) / try_divisor;
        // Get absolute difference from requested baud rate
        if ( baud_estimate < baudrate )
        {
          baud_diff = baudrate - baud_estimate;
        }
        else
        {
          baud_diff = baud_estimate - baudrate;
        }
        if ( i == 0 || baud_diff < best_baud_diff )
        {
          // Closest to requested baud rate so far
          best_divisor = try_divisor;
          best_baud = baud_estimate;
          best_baud_diff = baud_diff;
          if ( baud_diff == 0 )
          {
            // Spot on! No point trying
            break;
          }
        }
      }
      // Encode the best divisor value
      encoded_divisor = ( best_divisor >> 3 )
        | ( frac_code[ best_divisor & 7 ] << 14 );
      // Deal with special cases for encoded value
      if ( encoded_divisor == 1 )
      {
        encoded_divisor = 0;    // 3000000 baud
      }
      else if ( encoded_divisor == 0x4001 )
      {
        encoded_divisor = 1;    // 2000000 baud (BM only)
      }
      // Split into "value" and "index" values
      *value = (unsigned short) ( encoded_divisor & 0xFFFF );
      if ( ftdi->type == TYPE_2232C )
      {
        *index = (unsigned short) ( encoded_divisor >> 8 );
        *index &= 0xFF00;
        *index |= ftdi->index;
      }
      else
        *index = (unsigned short) ( encoded_divisor >> 16 );
    
      // Return the nearest baud rate
      return best_baud;
    }
    
    /**
     Sets the chip baud rate
    
     \param ftdi pointer to ftdi_context
     \param baudrate baud rate to set
    
     \retval  0: all fine
     \retval -1: invalid baudrate
     \retval -2: setting baudrate failed
     */
    int ftdi_set_baudrate( struct ftdi_context *ftdi, int baudrate )
    {
      unsigned short value, index;
      int actual_baudrate;
    
      if ( ftdi->bitbang_enabled )
      {
        baudrate = baudrate * 4;
      }
    
      actual_baudrate = ftdi_convert_baudrate( baudrate, ftdi, &value, &index );
      if ( actual_baudrate <= 0 )
        ftdi_error_return( -1, "Silly baudrate <= 0." );
    
      // Check within tolerance (about 5%)
      if ( ( actual_baudrate * 2 < baudrate /* Catch overflows */)
        || (
            ( actual_baudrate < baudrate ) ?
              ( actual_baudrate * 21 < baudrate * 20 ) :
              ( baudrate * 21 < actual_baudrate * 20 ) ) )
        ftdi_error_return( -1,
          "Unsupported baudrate. Note: bitbang baudrates are automatically multiplied by 4" );
    
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 3, value, index, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -2, "Setting new baudrate failed" );
    
      ftdi->baudrate = baudrate;
      return 0;
    }
    
    /**
     Set (RS232) line characteristics by Alain Abbas
    
     \param ftdi pointer to ftdi_context
     \param bits Number of bits
     \param sbit Number of stop bits
     \param parity Parity mode
    
     \retval  0: all fine
     \retval -1: Setting line property failed
     */
    int ftdi_set_line_property( struct ftdi_context *ftdi, enum ftdi_bits_type bits,
      enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity )
    {
      unsigned short value = bits;
    
      switch ( parity )
      {
        case NONE:
          value |= ( 0x00 << 8 );
          break;
        case ODD:
          value |= ( 0x01 << 8 );
          break;
        case EVEN:
          value |= ( 0x02 << 8 );
          break;
        case MARK:
          value |= ( 0x03 << 8 );
          break;
        case SPACE:
          value |= ( 0x04 << 8 );
          break;
      }
    
      switch ( sbit )
      {
        case STOP_BIT_1:
          value |= ( 0x00 << 11 );
          break;
        case STOP_BIT_15:
          value |= ( 0x01 << 11 );
          break;
        case STOP_BIT_2:
          value |= ( 0x02 << 11 );
          break;
      }
    
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x04, value, ftdi->index, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1, "Setting new line property failed" );
    
      return 0;
    }
    
    /**
     Writes data in chunks (see ftdi_write_data_set_chunksize()) to the chip
    
     \param ftdi pointer to ftdi_context
     \param buf Buffer with the data
     \param size Size of the buffer
    
     \retval <0: error code from usb_bulk_write()
     \retval >0: number of bytes written
     */
    int ftdi_write_data( struct ftdi_context *ftdi, unsigned char *buf, int size )
    {
      int ret;
      int offset = 0;
      int total_written = 0;
    
      while ( offset < size )
      {
        int write_size = ftdi->writebuffer_chunksize;
    
        if ( offset + write_size > size )
          write_size = size - offset;
    
        ret = usb_bulk_write( ftdi->usb_dev, ftdi->in_ep, buf + offset, write_size,
          ftdi->usb_write_timeout );
        if ( ret < 0 )
          ftdi_error_return( ret, "usb bulk write failed" );
    
        total_written += ret;
        offset += write_size;
      }
    
      return total_written;
    }
    
    /**
     Configure write buffer chunk size.
     Default is 4096.
    
     \param ftdi pointer to ftdi_context
     \param chunksize Chunk size
    
     \retval 0: all fine
     */
    int ftdi_write_data_set_chunksize( struct ftdi_context *ftdi,
      unsigned int chunksize )
    {
      ftdi->writebuffer_chunksize = chunksize;
      return 0;
    }
    
    /**
     Get write buffer chunk size.
    
     \param ftdi pointer to ftdi_context
     \param chunksize Pointer to store chunk size in
    
     \retval 0: all fine
     */
    int ftdi_write_data_get_chunksize( struct ftdi_context *ftdi,
      unsigned int *chunksize )
    {
      *chunksize = ftdi->writebuffer_chunksize;
      return 0;
    }
    
    /**
     Reads data in chunks (see ftdi_read_data_set_chunksize()) from the chip.
    
     Automatically strips the two modem status bytes transfered during every read.
    
     \param ftdi pointer to ftdi_context
     \param buf Buffer to store data in
     \param size Size of the buffer
    
     \retval <0: error code from usb_bulk_read()
     \retval =0: no data was available
     \retval >0: number of bytes read
    
     \remark This function is not useful in bitbang mode.
     Use ftdi_read_pins() to get the current state of the pins.
     */
    int ftdi_read_data( struct ftdi_context *ftdi, unsigned char *buf, int size )
    {
      int offset = 0, ret = 1, i, num_of_chunks, chunk_remains;
    
      // everything we want is still in the readbuffer?
      if ( size <= ftdi->readbuffer_remaining )
      {
        memcpy( buf, ftdi->readbuffer + ftdi->readbuffer_offset, size );
    
        // Fix offsets
        ftdi->readbuffer_remaining -= size;
        ftdi->readbuffer_offset += size;
    
        /* printf("Returning bytes from buffer: %d - remaining: %d\n", size, ftdi->readbuffer_remaining); */
    
        return size;
      }
      // something still in the readbuffer, but not enough to satisfy 'size'?
      if ( ftdi->readbuffer_remaining != 0 )
      {
        memcpy( buf, ftdi->readbuffer + ftdi->readbuffer_offset,
          ftdi->readbuffer_remaining );
    
        // Fix offset
        offset += ftdi->readbuffer_remaining;
      }
      // do the actual USB read
      while ( offset < size && ret > 0 )
      {
        ftdi->readbuffer_remaining = 0;
        ftdi->readbuffer_offset = 0;
        /* returns how much received */
        ret = usb_bulk_read( ftdi->usb_dev, ftdi->out_ep, ftdi->readbuffer,
          ftdi->readbuffer_chunksize, ftdi->usb_read_timeout );
        if ( ret < 0 )
          ftdi_error_return( ret, "usb bulk read failed" );
    
        if ( ret > 2 )
        {
          // skip FTDI status bytes.
          // Maybe stored in the future to enable modem use
          num_of_chunks = ret / 64;
          chunk_remains = ret % 64;
          //printf("ret = %X, num_of_chunks = %X, chunk_remains = %X, readbuffer_offset = %X\n", ret, num_of_chunks, chunk_remains, ftdi->readbuffer_offset);
    
          ftdi->readbuffer_offset += 2;
          ret -= 2;
    
          if ( ret > 62 )
          {
            for ( i = 1; i < num_of_chunks; i++ )
              memmove( ftdi->readbuffer + ftdi->readbuffer_offset + 62 * i,
                ftdi->readbuffer + ftdi->readbuffer_offset + 64 * i, 62 );
            if ( chunk_remains > 2 )
            {
              memmove( ftdi->readbuffer + ftdi->readbuffer_offset + 62 * i,
                ftdi->readbuffer + ftdi->readbuffer_offset + 64 * i,
                chunk_remains - 2 );
              ret -= 2 * num_of_chunks;
            }
            else
              ret -= 2 * ( num_of_chunks - 1 ) + chunk_remains;
          }
        }
        else if ( ret <= 2 )
        {
          // no more data to read?
          return offset;
        }
        if ( ret > 0 )
        {
          // data still fits in buf?
          if ( offset + ret <= size )
          {
            memcpy( buf + offset, ftdi->readbuffer + ftdi->readbuffer_offset, ret );
            //printf("buf[0] = %X, buf[1] = %X\n", buf[0], buf[1]);
            offset += ret;
    
            /* Did we read exactly the right amount of bytes? */
            if ( offset == size )
              //printf("read_data exact rem %d offset %d\n",
              //ftdi->readbuffer_remaining, offset);
              return offset;
          }
          else
          {
            // only copy part of the data or size <= readbuffer_chunksize
            int part_size = size - offset;
            memcpy( buf + offset, ftdi->readbuffer + ftdi->readbuffer_offset,
              part_size );
    
            ftdi->readbuffer_offset += part_size;
            ftdi->readbuffer_remaining = ret - part_size;
            offset += part_size;
    
            /* printf("Returning part: %d - size: %d - offset: %d - ret: %d - remaining: %d\n",
             part_size, size, offset, ret, ftdi->readbuffer_remaining); */
    
            return offset;
          }
        }
      }
      // never reached
      return -127;
    }
    
    /**
     Configure read buffer chunk size.
     Default is 4096.
    
     Automatically reallocates the buffer.
    
     \param ftdi pointer to ftdi_context
     \param chunksize Chunk size
    
     \retval 0: all fine
     */
    int ftdi_read_data_set_chunksize( struct ftdi_context *ftdi,
      unsigned int chunksize )
    {
      unsigned char *new_buf;
    
      // Invalidate all remaining data
      ftdi->readbuffer_offset = 0;
      ftdi->readbuffer_remaining = 0;
    
      if ( ( new_buf = (unsigned char *) realloc( ftdi->readbuffer, chunksize ) )
        == NULL )
        ftdi_error_return( -1, "out of memory for readbuffer" );
    
      ftdi->readbuffer = new_buf;
      ftdi->readbuffer_chunksize = chunksize;
    
      return 0;
    }
    
    /**
     Get read buffer chunk size.
    
     \param ftdi pointer to ftdi_context
     \param chunksize Pointer to store chunk size in
    
     \retval 0: all fine
     */
    int ftdi_read_data_get_chunksize( struct ftdi_context *ftdi,
      unsigned int *chunksize )
    {
      *chunksize = ftdi->readbuffer_chunksize;
      return 0;
    }
    
    /**
     Enable bitbang mode.
    
     For advanced bitbang modes of the FT2232C chip use ftdi_set_bitmode().
    
     \param ftdi pointer to ftdi_context
     \param bitmask Bitmask to configure lines.
     HIGH/ON value configures a line as output.
    
     \retval  0: all fine
     \retval -1: can't enable bitbang mode
     */
    int ftdi_enable_bitbang( struct ftdi_context *ftdi, unsigned char bitmask )
    {
      unsigned short usb_val;
    
      usb_val = bitmask; // low byte: bitmask
      /* FT2232C: Set bitbang_mode to 2 to enable SPI */
      usb_val |= ( ftdi->bitbang_mode << 8 );
    
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x0B, usb_val, ftdi->index, NULL,
        0, ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1,
          "unable to enter bitbang mode. Perhaps not a BM type chip?" );
    
      ftdi->bitbang_enabled = 1;
      return 0;
    }
    
    /**
     Disable bitbang mode.
    
     \param ftdi pointer to ftdi_context
    
     \retval  0: all fine
     \retval -1: can't disable bitbang mode
     */
    int ftdi_disable_bitbang( struct ftdi_context *ftdi )
    {
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x0B, 0, ftdi->index, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1,
          "unable to leave bitbang mode. Perhaps not a BM type chip?" );
    
      ftdi->bitbang_enabled = 0;
      return 0;
    }
    
    /**
     Enable advanced bitbang mode for FT2232C chips.
    
     \param ftdi pointer to ftdi_context
     \param bitmask Bitmask to configure lines.
     HIGH/ON value configures a line as output.
     \param mode Bitbang mode: 1 for normal mode, 2 for SPI mode
    
     \retval  0: all fine
     \retval -1: can't enable bitbang mode
     */
    int ftdi_set_bitmode( struct ftdi_context *ftdi, unsigned char bitmask,
      unsigned char mode )
    {
      unsigned short usb_val;
    
      usb_val = bitmask; // low byte: bitmask
      usb_val |= ( mode << 8 );
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x0B, usb_val, ftdi->index, NULL,
        0, ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1,
          "unable to configure bitbang mode. Perhaps not a 2232C type chip?" );
    
      ftdi->bitbang_mode = mode;
      ftdi->bitbang_enabled =
          ( mode == BITMODE_BITBANG || mode == BITMODE_SYNCBB ) ? 1 : 0;
      return 0;
    }
    
    /**
     Directly read pin state. Useful for bitbang mode.
    
     \param ftdi pointer to ftdi_context
     \param pins Pointer to store pins into
    
     \retval  0: all fine
     \retval -1: read pins failed
     */
    int ftdi_read_pins( struct ftdi_context *ftdi, unsigned char *pins )
    {
      if ( usb_control_msg( ftdi->usb_dev, 0xC0, 0x0C, 0, ftdi->index,
        (char *) pins, 1, ftdi->usb_read_timeout ) != 1 )
        ftdi_error_return( -1, "read pins failed" );
    
      return 0;
    }
    
    /**
     Set latency timer
    
     The FTDI chip keeps data in the internal buffer for a specific
     amount of time if the buffer is not full yet to decrease
     load on the usb bus.
    
     \param ftdi pointer to ftdi_context
     \param latency Value between 1 and 255
    
     \retval  0: all fine
     \retval -1: latency out of range
     \retval -2: unable to set latency timer
     */
    int ftdi_set_latency_timer( struct ftdi_context *ftdi, unsigned char latency )
    {
      unsigned short usb_val;
    
      if ( latency < 1 )
        ftdi_error_return( -1, "latency out of range. Only valid for 1-255" );
    
      usb_val = latency;
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x09, usb_val, ftdi->index, NULL,
        0, ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -2, "unable to set latency timer" );
    
      return 0;
    }
    
    /**
     Get latency timer
    
     \param ftdi pointer to ftdi_context
     \param latency Pointer to store latency value in
    
     \retval  0: all fine
     \retval -1: unable to get latency timer
     */
    int ftdi_get_latency_timer( struct ftdi_context *ftdi, unsigned char *latency )
    {
      unsigned short usb_val;
      if ( usb_control_msg( ftdi->usb_dev, 0xC0, 0x0A, 0, ftdi->index,
        (char *) &usb_val, 1, ftdi->usb_read_timeout ) != 1 )
        ftdi_error_return( -1, "reading latency timer failed" );
    
      *latency = (unsigned char) usb_val;
      return 0;
    }
    
    /**
     Init eeprom with default values.
    
     \param eeprom Pointer to ftdi_eeprom
     */
    void ftdi_eeprom_initdefaults( struct ftdi_eeprom *eeprom )
    {
      eeprom->vendor_id = 0x0403;
      eeprom->product_id = 0x6001;
      eeprom->device_id = 0x0000;
    
      eeprom->byte_8_bit_0 = 0;
      eeprom->byte_8_bit_1 = 0;
      eeprom->byte_8_bit_2 = 0;
      eeprom->byte_8_bit_3 = 0;
      eeprom->battery_powered = 0;
      eeprom->remote_wakeup = 1;
      eeprom->self_powered = 1;
      eeprom->must_be_set = 1;
    
      // Additional USB Configuration Attributes
      eeprom->chA_in_is_isochronous = 0;
      eeprom->chA_out_is_isochronous = 0;
      eeprom->suspend_pull_downs = 0;
      eeprom->use_serial_nr = 1;
      eeprom->use_usb_version = 1;
      eeprom->chB_in_is_isochronous = 0;
      eeprom->chB_out_is_isochronous = 0;
      eeprom->byte_A_bit_7 = 0;
    
      eeprom->byte_B_bit_0 = 0;
      eeprom->byte_B_bit_1 = 0;
      eeprom->byte_B_bit_2 = 0;
      eeprom->byte_B_bit_3 = 0;
      eeprom->byte_B_bit_4 = 0;
      eeprom->byte_B_bit_5 = 0;
      eeprom->byte_B_bit_6 = 0;
      eeprom->byte_B_bit_7 = 0;
    
      eeprom->byte_0x14 = 0;
      eeprom->byte_0x15 = 0;
    
      eeprom->usb_version = 0x0200;
    
      eeprom->max_power = 0;
    
      eeprom->manufacturer = NULL;
      eeprom->product = NULL;
      eeprom->serial_nr = NULL;
    
      // Channel A Attributes
      eeprom->chA_is_FIFO = 0;
      eeprom->chA_is_CPU_FIFO_target = 0;
      eeprom->chA_is_OPTO = 0;
      eeprom->chA_is_VCP = 0;
      eeprom->chA_is_high_current = 0;
      eeprom->byte_0_bit_5 = 0;
      eeprom->byte_0_bit_6 = 0;
      eeprom->byte_0_bit_7 = 0;
    
      // Channel B Attributes
      eeprom->chB_is_FIFO = 0;
      eeprom->chB_is_CPU_FIFO_target = 0;
      eeprom->chB_is_OPTO = 0;
      eeprom->chB_is_VCP = 0;
      eeprom->chB_is_high_current = 0;
      eeprom->byte_1_bit_5 = 0;
      eeprom->byte_1_bit_6 = 0;
      eeprom->byte_1_bit_7 = 0;
    }
    
    /**
     Build binary output from ftdi_eeprom structure.
     Output is suitable for ftdi_write_eeprom().
    
     \param ftdi Pointer to ftdi_context
     \param eeprom Pointer to ftdi_eeprom
     \param output Buffer of 128 bytes to store eeprom image to
    
     \retval >0: used eeprom size
     \retval -1: eeprom size (128 bytes) exceeded by custom strings
     */
    int ftdi_eeprom_build( struct ftdi_context *ftdi, struct ftdi_eeprom *eeprom,
      unsigned char *output )
    {
      unsigned char i, j;
      unsigned short checksum, value;
      unsigned char manufacturer_size = 0, product_size = 0, serial_nr_size = 0;
      int size_check;
    
      if ( eeprom->manufacturer != NULL )
        manufacturer_size = strlen( eeprom->manufacturer );
      if ( eeprom->product != NULL )
        product_size = strlen( eeprom->product );
      if ( eeprom->serial_nr != NULL )
        serial_nr_size = strlen( eeprom->serial_nr );
    
      if ( ftdi->type == TYPE_2232C )
      {
        memset( output, 0, 256 ); // empty eeprom
        size_check = 256 - 0x9E; // bytes 0x14 and 0x15 and 1st 0x80 are automatically used
      }
      else
      {
        memset( output, 0, 128 ); // empty eeprom
        size_check = 128 - 0x1C;
      }
    
      size_check -= manufacturer_size * 2;
      size_check -= product_size * 2;
      size_check -= serial_nr_size * 2;
    
      // eeprom size exceeded?
      if ( size_check < 0 )
        return ( -1 );
    
      // Addr 00: Stay 00 00
      // Channel A Attributes
      //    You can tell it is in UART mode because data pin 0 stays high when UART is idle
      //    If bits 0-2 of this byte are zero, it is UART mode
      j = 0;
      if ( eeprom->chA_is_FIFO == 1 )
        j = j | 0x01;
      if ( eeprom->chA_is_CPU_FIFO_target == 1 )
        j = j | 0x02;
      if ( eeprom->chA_is_OPTO == 1 )
        j = j | 0x04;
      if ( eeprom->chA_is_VCP == 1 )
        j = j | 0x08;
      if ( eeprom->chA_is_high_current == 1 )
        j = j | 0x10;
      if ( eeprom->byte_0_bit_5 == 1 )
        j = j | 0x20;
      if ( eeprom->byte_0_bit_6 == 1 )
        j = j | 0x40;
      if ( eeprom->byte_0_bit_7 == 1 )
        j = j | 0x80;
      output[ 0x00 ] = j;
    
      // Channel B Attributes
      //    You can tell it is in UART mode because data pin 0 stays high when UART is idle
      //    If bits 0-2 are zero, it is UART mode
      j = 0;
      if ( eeprom->chB_is_FIFO == 1 )
        j = j | 0x01;
      if ( eeprom->chB_is_CPU_FIFO_target == 1 )  // 2232D, but not 2232C
        j = j | 0x02;
      if ( eeprom->chB_is_OPTO == 1 )
        j = j | 0x04;
      if ( eeprom->chB_is_VCP == 1 )
        j = j | 0x08;
      if ( eeprom->chB_is_high_current == 1 )
        j = j | 0x10;
      if ( eeprom->byte_1_bit_5 == 1 )
        j = j | 0x20;
      if ( eeprom->byte_1_bit_6 == 1 )
        j = j | 0x40;
      if ( eeprom->byte_1_bit_7 == 1 )
        j = j | 0x80;
      output[ 0x01 ] = j;
    
      // Addr 02: Vendor ID
      output[ 0x02 ] = eeprom->vendor_id;
      output[ 0x03 ] = eeprom->vendor_id >> 8;
    
      // Addr 04: Product ID
      output[ 0x04 ] = eeprom->product_id;
      output[ 0x05 ] = eeprom->product_id >> 8;
    
      // Addr 06: Device release number (0400h for BM features)
      if ( eeprom->device_id == 0 )
      {
        switch ( ftdi->type )
        {
          case TYPE_OLD:
            eeprom->device_id = 0x0000;
            break;
          case TYPE_AM:
            eeprom->device_id = 0x0200;
            break;
          case TYPE_BM:
            eeprom->device_id = 0x0400;
            break;
          case TYPE_2232C:
            eeprom->device_id = 0x0500;
            break;
          default:
            return ( -2 );
        }
      }
      // Device or version number
      output[ 0x06 ] = eeprom->device_id;
      output[ 0x07 ] = eeprom->device_id >> 8;
      ;
    
      if ( ftdi->type == TYPE_2232C )
      {
        // Standard USB Configuration Descriptor Attributes
        // Bit 4: Battery powered if 1
        // Bit 5: Remote wakeup if 1
        // Bit 6: Self powered if 1
        // Bit 7: This bit must be set for historical reasons
        j = 0;
        if ( eeprom->byte_8_bit_0 == 1 )
          j = j | 0x01;
        if ( eeprom->byte_8_bit_1 == 1 )
          j = j | 0x02;
        if ( eeprom->byte_8_bit_2 == 1 )
          j = j | 0x04;
        if ( eeprom->byte_8_bit_3 == 1 )
          j = j | 0x08;
        if ( eeprom->battery_powered == 1 ) // Plug 'n Play (whatever that means) for FT232 chips
          j = j | 0x10;
        if ( eeprom->remote_wakeup == 1 )
          j = j | 0x20;
        if ( eeprom->self_powered == 1 )
          j = j | 0x40;
        if ( eeprom->must_be_set == 1 )
          j = j | 0x80;
        output[ 0x08 ] = j;
      }
      else
      {
        // Addr 08: Config descriptor
        // Bit 1: remote wakeup if 1
        // Bit 0: self powered if 1
        //
        j = 0;
        if ( eeprom->self_powered == 1 )
          j = j | 1;
        if ( eeprom->remote_wakeup == 1 )
          j = j | 2;
        output[ 0x08 ] = j;
      }
    
      // Addr 09: Max power consumption: max power = value * 2 mA
      output[ 0x09 ] = eeprom->max_power;
    
      // Addr 0A: Additional USB Configuration Attributes
      // Bit 7: 0 - reserved and must be set to 1
      // Bit 6: 0 - Channel B out end point is isosynchronous
      // Bit 5: 0 - Channel B in end point is isosynchronous
      // Bit 4: 1 - Change USB version
      // Bit 3: 1 - Use the serial number string
      // Bit 2: 1 - Enable suspend pull downs for lower power
      // Bit 1: 1 - Channel A out EndPoint is Isochronous
      // Bit 0: 1 - Channel A in EndPoint is Isochronous
      j = 0;
      if ( eeprom->chA_in_is_isochronous == 1 )
        j = j | 0x01;
      if ( eeprom->chA_out_is_isochronous == 1 )
        j = j | 0x02;
      if ( eeprom->suspend_pull_downs == 1 )
        j = j | 0x04;
      if ( eeprom->use_serial_nr == 1 )
        j = j | 0x08;
      if ( eeprom->use_usb_version == 1 )
        j = j | 0x10;
      if ( eeprom->chB_in_is_isochronous == 1 )
        j = j | 0x20;
      if ( eeprom->chB_out_is_isochronous == 1 )
        j = j | 0x40;
      if ( eeprom->byte_A_bit_7 == 1 )
        j = j | 0x80;
      output[ 0x0A ] = j;
    
      // Addr 0B: reserved
      output[ 0x0B ] = 0x00;
      j = 0;
      if ( eeprom->byte_B_bit_0 == 1 )
        j = j | 1;
      if ( eeprom->byte_B_bit_1 == 1 )
        j = j | 2;
      if ( eeprom->byte_B_bit_2 == 1 )
        j = j | 4;
      if ( eeprom->byte_B_bit_3 == 1 )
        j = j | 8;
      if ( eeprom->byte_B_bit_4 == 1 )
        j = j | 16;
      if ( eeprom->byte_B_bit_5 == 1 )
        j = j | 32;
      if ( eeprom->byte_B_bit_6 == 1 )
        j = j | 64;
      if ( eeprom->byte_B_bit_7 == 1 )
        j = j | 128;
      output[ 0x0B ] = j;
    
      // 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->use_usb_version == 1 )
      {
        output[ 0x0C ] = eeprom->usb_version;
        output[ 0x0D ] = eeprom->usb_version >> 8;
      }
    
      // The string locations are different in the FT2232C and the Unicode byte order id reversed.
      // Also there two additional bytes after the string pointers.
      if ( ftdi->type == TYPE_2232C )
      {
        // Addr 0E: Offset of the manufacturer string
        i = 0x0E;
        output[ i++ ] = 0x80 + 0x16;           // manufacturer offset - default 0x5D
        output[ i++ ] = strlen( eeprom->manufacturer ) * 2 + 2; // manufacturer length - default 0x0A
    
        output[ i++ ] = output[ 0x0E ] + 2 + strlen( eeprom->manufacturer ) * 2; // product offset - default 0x67
        output[ i++ ] = strlen( eeprom->product ) * 2 + 2; // product length - default 0x16
    
        output[ i++ ] = output[ 0x10 ] + 2 + strlen( eeprom->product ) * 2; // serial offset - default 0x00 to show first line of PROM
        output[ i++ ] = strlen( eeprom->serial_nr ) * 2 + 2; // serial offset - default 0x12 to show full 16 bytes
    
        output[ i++ ] = eeprom->byte_0x14;     // Unknown, has been seen set to 0x56
        output[ i++ ] = eeprom->byte_0x15;     // Unknown, has been seen set to 0x00
    
        i += 0x80;
        output[ i++ ] = output[ 0x0F ];         // Length of manufacturer string
        output[ i++ ] = 0x03;                 // USB string descriptor code
        for ( j = 0; j < strlen( eeprom->manufacturer ); j++ )
        {
          output[ i++ ] = eeprom->manufacturer[ j ];
          output[ i++ ] = 0x00;
        }
    
        output[ i++ ] = output[ 0x11 ];         // Length of product string
        output[ i++ ] = 0x03;                 // USB string descriptor code
        for ( j = 0; j < strlen( eeprom->product ); j++ )
        {
          output[ i++ ] = eeprom->product[ j ];
          output[ i++ ] = 0x00;
        }
    
        output[ i++ ] = output[ 0x13 ];         // Length of serial number string
        output[ i++ ] = 0x03;                 // USB string descriptor code
        for ( j = 0; j < strlen( eeprom->serial_nr ); j++ )
        {
          output[ i++ ] = eeprom->serial_nr[ j ];
          output[ i++ ] = 0x00;
        }
      }
      else
      {   // AM and BM chips
        // Addr 0E: Offset of the manufacturer string + 0x80
        output[ 0x0E ] = 0x14 + 0x80;
    
        // Addr 0F: Length of manufacturer string
        output[ 0x0F ] = manufacturer_size * 2 + 2;
    
        // Addr 10: Offset of the product string + 0x80, calculated later
        // Addr 11: Length of product string
        output[ 0x11 ] = product_size * 2 + 2;
    
        // Addr 12: Offset of the serial string + 0x80, calculated later
        // Addr 13: Length of serial string
        output[ 0x13 ] = serial_nr_size * 2 + 2;
    
        // Dynamic content
        output[ 0x14 ] = manufacturer_size * 2 + 2;
        output[ 0x15 ] = 0x03; // type: string
    
        i = 0x16, j = 0;
    
        // Output manufacturer
        for ( j = 0; j < manufacturer_size; j++ )
        {
          output[ i ] = eeprom->manufacturer[ j ], i++;
          output[ i ] = 0x00, i++;
        }
    
        // Output product name
        output[ 0x10 ] = i + 0x80;  // calculate offset
        output[ i ] = product_size * 2 + 2, i++;
        output[ i ] = 0x03, i++;
        for ( j = 0; j < product_size; j++ )
        {
          output[ i ] = eeprom->product[ j ], i++;
          output[ i ] = 0x00, i++;
        }
    
        // Output serial
        output[ 0x12 ] = i + 0x80; // calculate offset
        output[ i ] = serial_nr_size * 2 + 2, i++;
        output[ i ] = 0x03, i++;
        for ( j = 0; j < serial_nr_size; j++ )
        {
          output[ i ] = eeprom->serial_nr[ j ], i++;
          output[ i ] = 0x00, i++;
        }
      }
      // calculate checksum
      checksum = 0xAAAA;
    
      if ( ftdi->type == TYPE_2232C )
        j = 127;
      else
        j = 63;
    
      for ( i = 0; i < j; i++ )
      {
        value = output[ i * 2 ];
        value += output[ ( i * 2 ) + 1 ] << 8;
    
        checksum = value ^ checksum;
        checksum = ( checksum << 1 ) | ( checksum >> 15 );
      }
    
      j = j << 1;
      output[ j++ ] = checksum;
      output[ j ] = checksum >> 8;
    
      return size_check;
    }
    
    /**
     Read eeprom
    
     \param ftdi pointer to ftdi_context
     \param eeprom Pointer to store eeprom into
    
     \retval  0: all fine
     \retval -1: read failed
     */
    int ftdi_read_eeprom( struct ftdi_context *ftdi, unsigned char *eeprom )
    {
      int i, words;
    
      if ( ftdi->type == TYPE_2232C )
        words = 128;
      else
        words = 64;
    
      for ( i = 0; i < words; i++ )
      {
        if ( usb_control_msg( ftdi->usb_dev, 0xC0, 0x90, 0, i, eeprom + ( i * 2 ),
          2, ftdi->usb_read_timeout ) != 2 )
          ftdi_error_return( -1, "reading eeprom failed" );
      }
    
      return 0;
    }
    
    /**
     Write eeprom
    
     \param ftdi pointer to ftdi_context
     \param eeprom Pointer to read eeprom from
    
     \retval  0: all fine
     \retval -1: read failed
     */
    int ftdi_write_eeprom( struct ftdi_context *ftdi, unsigned char *eeprom )
    {
      unsigned short usb_val;
      int i, words;
    
      if ( ftdi->type == TYPE_2232C )
        words = 128;
      else
        words = 64;
    
      for ( i = 0; i < words; i++ )
      {
        usb_val = eeprom[ i * 2 ];
        usb_val += eeprom[ ( i * 2 ) + 1 ] << 8;
        if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x91, usb_val, i, NULL, 0,
          ftdi->usb_write_timeout ) != 0 )
          ftdi_error_return( -1, "unable to write eeprom" );
      }
    
      return 0;
    }
    
    /**
     Erase eeprom
    
     \param ftdi pointer to ftdi_context
    
     \retval  0: all fine
     \retval -1: erase failed
     */
    int ftdi_erase_eeprom( struct ftdi_context *ftdi )
    {
      if ( usb_control_msg( ftdi->usb_dev, 0x40, 0x92, 0, 0, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1, "unable to erase eeprom" );
    
      return 0;
    }
    
    /**
     Get string representation for last error code
    
     \param ftdi pointer to ftdi_context
    
     \retval Pointer to error string
     */
    char *ftdi_get_error_string( struct ftdi_context *ftdi )
    {
      return ftdi->error_str;
    }
    
    /*
     Flow control code by Lorenz Moesenlechner (lorenz@hcilab.org)
     and Matthias Kranz  (matthias@hcilab.org)
     */
    /**
     Set flowcontrol for ftdi chip
    
     \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   
    
     \retval  0: all fine
     \retval -1: set flow control failed
     */
    int ftdi_setflowctrl( struct ftdi_context *ftdi, int flowctrl )
    {
      if ( usb_control_msg( ftdi->usb_dev, SIO_SET_FLOW_CTRL_REQUEST_TYPE,
        SIO_SET_FLOW_CTRL_REQUEST, 0, ( flowctrl | ftdi->interface ), 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
     \param state state to set line to (1 or 0)
    
     \retval  0: all fine
     \retval -1: set dtr failed
     */
    int ftdi_setdtr( struct ftdi_context *ftdi, int state )
    {
      unsigned short usb_val;
    
      if ( state )
        usb_val = SIO_SET_DTR_HIGH;
      else
        usb_val = SIO_SET_DTR_LOW;
    
      if ( usb_control_msg( ftdi->usb_dev, SIO_SET_MODEM_CTRL_REQUEST_TYPE,
        SIO_SET_MODEM_CTRL_REQUEST, usb_val, ftdi->interface, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1, "set dtr failed" );
    
      return 0;
    }
    
    /**
     Set rts line
    
     \param ftdi pointer to ftdi_context
     \param state state to set line to (1 or 0)
    
     \retval  0: all fine
     \retval -1 set rts failed
     */
    int ftdi_setrts( struct ftdi_context *ftdi, int state )
    {
      unsigned short usb_val;
    
      if ( state )
        usb_val = SIO_SET_RTS_HIGH;
      else
        usb_val = SIO_SET_RTS_LOW;
    
      if ( usb_control_msg( ftdi->usb_dev, SIO_SET_MODEM_CTRL_REQUEST_TYPE,
        SIO_SET_MODEM_CTRL_REQUEST, usb_val, ftdi->interface, NULL, 0,
        ftdi->usb_write_timeout ) != 0 )
        ftdi_error_return( -1, "set of rts failed" );
    
      return 0;
    }
    
    /* @} end of doxygen libftdi group */
    /***************************************************************************
     ftdi_i.h  -  description
     -------------------
     begin                : Don Sep 9 2011
     copyright            : (C) 2003-2013 by Intra2net AG and the libftdi developers
     email                : opensource@intra2net.com
     ***************************************************************************/
    
    /***************************************************************************
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU Lesser General Public License           *
     *   version 2.1 as published by the Free Software Foundation;             *
     *                                                                         *
     ***************************************************************************
    
     Non public definitions here
    
     */
    
    /* Even on 93xx66 at max 256 bytes are used (AN_121)*/
    #define FTDI_MAX_EEPROM_SIZE 256
    
    #define MAX_POWER_MILLIAMP_PER_UNIT 2
    
    struct ftdi_eeprom
    {
      int vendor_id;
      int product_id;
    
      int initialized_for_connected_device;
    
      int self_powered;
      int remote_wakeup;
    
      int is_not_pnp;
    
      /* Suspend on DBUS7 Low */
      int suspend_dbus7;
    
      int in_is_isochronous;
      int out_is_isochronous;
      int suspend_pull_downs;
    
      int use_serial;
      int usb_version;
      int use_usb_version;
      int max_power;
    
      char *manufacturer;
      char *product;
      char *serial;
    
      /* 2232D/H specific */
      /* Hardware type, 0 = RS232 Uart, 1 = 245 FIFO, 2 = CPU FIFO, 
       4 = OPTO Isolate */
      int channel_a_type;
      int channel_b_type;
      /*  Driver Type, 1 = VCP */
      int channel_a_driver;
      int channel_b_driver;
      int channel_c_driver;
      int channel_d_driver;
      /* 4232H specific */
      int channel_a_rs485enable;
      int channel_b_rs485enable;
      int channel_c_rs485enable;
      int channel_d_rs485enable;
    
      /* Special function of FT232R/FT232H devices (and possibly others as well) */
      int cbus_function[ 10 ];
      int high_current;
      int high_current_a;
      int high_current_b;
      int invert;
    
      /*2232H/4432H Group specific values */
      /* Group0 is AL on 2322H and A on 4232H
       Group1 is AH on 2232H and B on 4232H
       Group2 is BL on 2322H and C on 4232H
       Group3 is BH on 2232H and C on 4232H*/
      int group0_drive;
      int group0_schmitt;
      int group0_slew;
      int group1_drive;
      int group1_schmitt;
      int group1_slew;
      int group2_drive;
      int group2_schmitt;
      int group2_slew;
      int group3_drive;
      int group3_schmitt;
      int group3_slew;
    
      int powersave;
    
      int clock_polarity;
      int data_order;
      int flow_control;
    
      int size;
      /* EEPROM Type 0x46 for 93xx46, 0x56 for 93xx56 and 0x66 for 93xx66*/
      int chip;
      unsigned char buf[ FTDI_MAX_EEPROM_SIZE ];
    };

  • 相关阅读:
    学习:ASP.NET中App_Code,App_Data等文件夹的作用(转)
    总结:CLR Via C#(第九章):属性与索引器
    总结:CLR Via C#(第八章):其他(方法传递、out、ref)
    Init Version
    Code 128 Barcode Font Advantage Package 的常见问题
    Skelta Workflow.NET 2004 R2
    GTP.NET 甘特图项目管理控件
    Code 128 Barcode Font Advantage Package 中如何手动加入起始符,结束符,校验符
    VectorDraw Professional v5.1.1.1043
    开篇
  • 原文地址:https://www.cnblogs.com/shangdawei/p/3093918.html
Copyright © 2020-2023  润新知