• FTDI EEPROM


    /***************************************************************************
     ftdi.c  -  description
     -------------------
     begin                : Fri Apr 4 2003
     copyright            : (C) 2003 by Intra2net AG
     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;             *
     *                                                                         *
     ***************************************************************************/
    
    #include <usb.h>
    #include <string.h>
    
    #include "ftdi.h"
    
    #define ftdi_error_return(code, str) do {  \
            ftdi->error_str = str;             \
            return code;                       \
       } while(0);                 
    
    /* ftdi_init
    
     Initializes a ftdi_context.
    
     Return codes:
     0: All fine
     -1: Couldn't allocate read buffer
     */
    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 );
    }
    
    /* ftdi_set_interface
     
     Call after ftdi_init
     
     Open selected channels on a chip, otherwise use first channel
     0: all fine
     -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;
    }
    
    /* ftdi_deinit
    
     Deinitializes a ftdi_context.
     */
    void ftdi_deinit( struct ftdi_context *ftdi )
    {
      if ( ftdi->readbuffer != NULL )
      {
        free( ftdi->readbuffer );
        ftdi->readbuffer = NULL;
      }
    }
    
    /* ftdi_set_usbdev
     
     Use an already open device.
     */
    void ftdi_set_usbdev( struct ftdi_context *ftdi, usb_dev_handle *usb )
    {
      ftdi->usb_dev = usb;
    }
    
    /* ftdi_usb_find_all
     
     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.
    
     Return codes:
     >0: number of devices found
     -1: usb_find_busses() failed
     -2: usb_find_devices() failed
     -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;
      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;
    }
    
    /* ftdi_list_free
    
     Frees a created device list.
     */
    void ftdi_list_free( struct ftdi_device_list **devlist )
    {
      struct ftdi_device_list **curdev;
      for ( ; *devlist == NULL; devlist = curdev )
      {
        curdev = &( *devlist )->next;
        free( *devlist );
      }
    
      devlist = NULL;
    }
    
    /* ftdi_usb_open_dev 
    
     Opens a ftdi device given by a usb_device.
     
     Return codes:
     0: all fine
     -4: unable to open device
     -5: unable to claim device
     -6: reset failed
     -7: set baudrate failed
     */
    int ftdi_usb_open_dev( struct ftdi_context *ftdi, struct usb_device *dev )
    {
      if ( !( ftdi->usb_dev = usb_open( dev ) ) )
        ftdi_error_return( -4, "usb_open() failed" );
    
      if ( usb_claim_interface( ftdi->usb_dev, ftdi->interface ) != 0 )
      {
        usb_close( ftdi->usb_dev );
        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->descriptor.bcdDevice == 0x400
        || ( dev->descriptor.bcdDevice == 0x200
          && dev->descriptor.iSerialNumber == 0 ) )
        ftdi->type = TYPE_BM;
      else if ( dev->descriptor.bcdDevice == 0x200 )
        ftdi->type = TYPE_AM;
      else if ( dev->descriptor.bcdDevice == 0x500 )
      {
        ftdi->type = TYPE_2232C;
        if ( !ftdi->index )
          ftdi->index = INTERFACE_A;
      }
    
      ftdi_error_return( 0, "all fine" );
    }
    
    /* ftdi_usb_open
     
     Opens the first device with a given vendor and product ids.
     
     Return codes:
     See 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 );
    }
    
    /* ftdi_usb_open_desc
    
     Opens the first device with a given, vendor id, product id,
     description and serial.
     
     Return codes:
     0: all fine
     -1: usb_find_busses() failed
     -2: usb_find_devices() failed
     -3: usb device not found
     -4: unable to open device
     -5: unable to claim device
     -6: reset failed
     -7: set baudrate failed
     -8: get product description failed
     -9: get serial number failed
     -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" );
    }
    
    /* ftdi_usb_reset
    
     Resets the ftdi device.
     
     Return codes:
     0: all fine
     -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;
    }
    
    /* ftdi_usb_purge_buffers
    
     Cleans the buffers of the ftdi device.
     
     Return codes:
     0: all fine
     -1: write buffer purge failed
     -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;
    }
    
    /* ftdi_usb_close
     
     Closes the ftdi device.
     
     Return codes:
     0: all fine
     -1: usb_release failed
     -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
     */
    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;
    }
    
    /*
     ftdi_set_baudrate
     
     Sets the chip baudrate
     
     Return codes:
     0: all fine
     -1: invalid baudrate
     -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;
    }
    
    /*
     ftdi_set_line_property
    
     set (RS232) line characteristics by Alain Abbas
     
     Return codes:
     0: all fine
     -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;
    }
    
    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;
    }
    
    int ftdi_write_data_set_chunksize( struct ftdi_context *ftdi,
      unsigned int chunksize )
    {
      ftdi->writebuffer_chunksize = chunksize;
      return 0;
    }
    
    int ftdi_write_data_get_chunksize( struct ftdi_context *ftdi,
      unsigned int *chunksize )
    {
      *chunksize = ftdi->writebuffer_chunksize;
      return 0;
    }
    
    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;
    }
    
    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;
    }
    
    int ftdi_read_data_get_chunksize( struct ftdi_context *ftdi,
      unsigned int *chunksize )
    {
      *chunksize = ftdi->readbuffer_chunksize;
      return 0;
    }
    
    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;
    }
    
    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;
    }
    
    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;
    }
    
    int ftdi_read_pins( struct ftdi_context *ftdi, unsigned char *pins )
    {
      unsigned short usb_val;
      if ( usb_control_msg( ftdi->usb_dev, 0xC0, 0x0C, 0, ftdi->index,
        (char *) &usb_val, 1, ftdi->usb_read_timeout ) != 1 )
        ftdi_error_return( -1, "read pins failed" );
    
      *pins = (unsigned char) usb_val;
      return 0;
    }
    
    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;
    }
    
    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;
    }
    
    void ftdi_eeprom_initdefaults( struct ftdi_eeprom *eeprom )
    {
      eeprom->vendor_id = 0x0403;
      eeprom->product_id = 0x6001;
    
      eeprom->self_powered = 1;
      eeprom->remote_wakeup = 1;
      eeprom->BM_type_chip = 1;
    
      eeprom->in_is_isochronous = 0;
      eeprom->out_is_isochronous = 0;
      eeprom->suspend_pull_downs = 0;
    
      eeprom->use_serial = 0;
      eeprom->change_usb_version = 0;
      eeprom->usb_version = 0x0200;
      eeprom->max_power = 0;
    
      eeprom->manufacturer = NULL;
      eeprom->product = NULL;
      eeprom->serial = NULL;
    }
    
    /*
     ftdi_eeprom_build
     
     Build binary output from ftdi_eeprom structure.
     Output is suitable for ftdi_write_eeprom.
     
     Return codes:
     positive value: used eeprom size
     -1: eeprom size (128 bytes) exceeded by custom strings
     */
    int ftdi_eeprom_build( struct ftdi_eeprom *eeprom, unsigned char *output )
    {
      unsigned char i, j;
      unsigned short checksum, value;
      unsigned char manufacturer_size = 0, product_size = 0, serial_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 != NULL )
        serial_size = strlen( eeprom->serial );
    
      size_check = 128; // eeprom is 128 bytes
      size_check -= 28; // 28 are always in use (fixed)
      size_check -= manufacturer_size * 2;
      size_check -= product_size * 2;
      size_check -= serial_size * 2;
    
      // eeprom size exceeded?
      if ( size_check < 0 )
        return ( -1 );
    
      // empty eeprom
      memset( output, 0, 128 );
    
      // Addr 00: Stay 00 00
      // 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)
      output[ 0x06 ] = 0x00;
    
      if ( eeprom->BM_type_chip == 1 )
        output[ 0x07 ] = 0x04;
      else
        output[ 0x07 ] = 0x02;
    
      // 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: Chip configuration
      // Bit 7: 0 - reserved
      // Bit 6: 0 - reserved
      // Bit 5: 0 - reserved
      // 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 - Out EndPoint is Isochronous
      // Bit 0: 1 - In EndPoint is Isochronous
      //
      j = 0;
      if ( eeprom->in_is_isochronous == 1 )
        j = j | 1;
      if ( eeprom->out_is_isochronous == 1 )
        j = j | 2;
      if ( eeprom->suspend_pull_downs == 1 )
        j = j | 4;
      if ( eeprom->use_serial == 1 )
        j = j | 8;
      if ( eeprom->change_usb_version == 1 )
        j = j | 16;
      output[ 0x0A ] = j;
    
      // Addr 0B: reserved
      output[ 0x0B ] = 0x00;
    
      // Addr 0C: USB version low byte when 0x0A bit 4 is set
      // Addr 0D: USB version high byte when 0x0A bit 4 is set
      if ( eeprom->change_usb_version == 1 )
      {
        output[ 0x0C ] = eeprom->usb_version;
        output[ 0x0D ] = eeprom->usb_version >> 8;
      }
    
      // 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_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_size * 2 + 2, i++;
      output[ i ] = 0x03, i++;
      for ( j = 0; j < serial_size; j++ )
      {
        output[ i ] = eeprom->serial[ j ], i++;
        output[ i ] = 0x00, i++;
      }
    
      // calculate checksum
      checksum = 0xAAAA;
    
      for ( i = 0; i < 63; i++ )
      {
        value = output[ i * 2 ];
        value += output[ ( i * 2 ) + 1 ] << 8;
    
        checksum = value ^ checksum;
        checksum = ( checksum << 1 ) | ( checksum >> 15 );
      }
    
      output[ 0x7E ] = checksum;
      output[ 0x7F ] = checksum >> 8;
    
      return size_check;
    }
    
    int ftdi_read_eeprom( struct ftdi_context *ftdi, unsigned char *eeprom )
    {
      int i;
    
      for ( i = 0; i < 64; 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;
    }
    
    int ftdi_write_eeprom( struct ftdi_context *ftdi, unsigned char *eeprom )
    {
      unsigned short usb_val;
      int i;
    
      for ( i = 0; i < 64; 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;
    }
    
    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;
    }
    
    char *ftdi_get_error_string( struct ftdi_context *ftdi )
    {
      return ftdi->error_str;
    }
    
    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;
    }
    
    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;
    }
    
    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;
    }

  • 相关阅读:
    爱因斯坦谜题的真正答案
    Lucence 中的排序算法解析
    XML 解析中,如何排除控制字符
    如何在百度贴吧里加入自己的广告图片
    ASP.NET 不能调试的几种情况
    Zend_Search_Lucence 中用UTF8 编码建立索引的问题
    c++继承中的内存布局 <转>
    Effective C++ 学习笔记(20)
    Effective C++ 学习笔记(19)
    Effective C++ 学习笔记(17)
  • 原文地址:https://www.cnblogs.com/shangdawei/p/3093870.html
Copyright © 2020-2023  润新知