From e8d8c2facd010c6ef8a3fbeaae731864260db1e3 Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 20 Sep 2006 11:06:27 +0100 Subject: [PATCH] checkpoint --- upstream/libftdi/ftdi.c | 1083 +++++++++++++++++++++++++++++++++++++++++++++++ upstream/libftdi/ftdi.h | 259 ++++++++++++ 2 files changed, 1342 insertions(+) create mode 100644 upstream/libftdi/ftdi.c create mode 100644 upstream/libftdi/ftdi.h diff --git a/upstream/libftdi/ftdi.c b/upstream/libftdi/ftdi.c new file mode 100644 index 0000000..3c13dc8 --- /dev/null +++ b/upstream/libftdi/ftdi.c @@ -0,0 +1,1083 @@ +/*************************************************************************** + 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 +#include +#include + +#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) +{ + 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->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); + //printf("usbread %d\n", ret); + 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; +} diff --git a/upstream/libftdi/ftdi.h b/upstream/libftdi/ftdi.h new file mode 100644 index 0000000..a90ad98 --- /dev/null +++ b/upstream/libftdi/ftdi.h @@ -0,0 +1,259 @@ +/*************************************************************************** + ftdi.h - 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; * + * * + ***************************************************************************/ + +#ifndef __libftdi_h__ +#define __libftdi_h__ + +#include + +enum ftdi_chip_type { TYPE_AM=0, TYPE_BM=1, TYPE_2232C=2 }; +enum ftdi_parity_type { NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4 }; +enum ftdi_stopbits_type { STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2 }; +enum ftdi_bits_type { BITS_7=7, BITS_8=8 }; + +enum ftdi_mpsse_mode { + BITMODE_RESET = 0x00, + BITMODE_BITBANG= 0x01, + BITMODE_MPSSE = 0x02, + BITMODE_SYNCBB = 0x04, + BITMODE_MCU = 0x08, + BITMODE_OPTO = 0x10 +}; + +/* Port interface code for FT2232C */ +enum ftdi_interface { + INTERFACE_ANY = 0, + INTERFACE_A = 1, + INTERFACE_B = 2 +}; + +/* Shifting commands IN MPSSE Mode*/ +#define MPSSE_WRITE_NEG 0x01 /* Write TDI/DO on negative TCK/SK edge*/ +#define MPSSE_BITMODE 0x02 /* Write bits, not bytes */ +#define MPSSE_READ_NEG 0x04 /* Sample TDO/DI on negative TCK/SK edge */ +#define MPSSE_LSB 0x08 /* LSB first */ +#define MPSSE_DO_WRITE 0x10 /* Write TDI/DO */ +#define MPSSE_DO_READ 0x20 /* Read TDO/DI */ +#define MPSSE_WRITE_TMS 0x40 /* Write TMS/CS */ + +/* FTDI MPSSE commands */ +#define SET_BITS_LOW 0x80 +/*BYTE DATA*/ +/*BYTE Direction*/ +#define SET_BITS_HIGH 0x82 +/*BYTE DATA*/ +/*BYTE Direction*/ +#define GET_BITS_LOW 0x81 +#define GET_BITS_HIGH 0x83 +#define LOOPBACK_START 0x84 +#define LOOPBACK_END 0x85 +#define TCK_DIVISOR 0x86 +/* Value Low */ +/* Value HIGH */ /*rate is 12000000/((1+value)*2) */ +#define DIV_VALUE(rate) (rate > 6000000)?0:((6000000/rate -1) > 0xffff)? 0xffff: (6000000/rate -1) + +/* Commands in MPSSE and Host Emulation Mode */ +#define SEND_IMMEDIATE 0x87 +#define WAIT_ON_HIGH 0x88 +#define WAIT_ON_LOW 0x89 + +/* Commands in Host Emulation Mode */ +#define READ_SHORT 0x90 +/* Address_Low */ +#define READ_EXTENDED 0x91 +/* Address High */ +/* Address Low */ +#define WRITE_SHORT 0x92 +/* Address_Low */ +#define WRITE_EXTENDED 0x93 +/* Address High */ +/* Address Low */ + +/* Definitions for flow control */ +/* + * Flow control code adapted from Linux kernel sources + * by Lorenz Moesenlechner (lorenz@hcilab.org) and + * Matthias Kranz (matthias@hcilab.org) + * */ +#define SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */ + +#define SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 +#define SIO_SET_FLOW_CTRL_REQUEST SIO_SET_FLOW_CTRL + +#define SIO_DISABLE_FLOW_CTRL 0x0 +#define SIO_RTS_CTS_HS (0x1 << 8) +#define SIO_DTR_DSR_HS (0x2 << 8) +#define SIO_XON_XOFF_HS (0x4 << 8) + +#define SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 +#define SIO_SET_MODEM_CTRL_REQUEST SIO_MODEM_CTRL + +#define SIO_SET_DTR_MASK 0x1 +#define SIO_SET_DTR_HIGH ( 1 | ( SIO_SET_DTR_MASK << 8)) +#define SIO_SET_DTR_LOW ( 0 | ( SIO_SET_DTR_MASK << 8)) +#define SIO_SET_RTS_MASK 0x2 +#define SIO_SET_RTS_HIGH ( 2 | ( SIO_SET_RTS_MASK << 8 )) +#define SIO_SET_RTS_LOW ( 0 | ( SIO_SET_RTS_MASK << 8 )) + +#define SIO_RTS_CTS_HS (0x1 << 8) + + +struct ftdi_context { + // USB specific + struct usb_dev_handle *usb_dev; + int usb_read_timeout; + int usb_write_timeout; + + // FTDI specific + enum ftdi_chip_type type; + int baudrate; + unsigned char bitbang_enabled; + unsigned char *readbuffer; + unsigned int readbuffer_offset; + unsigned int readbuffer_remaining; + unsigned int readbuffer_chunksize; + unsigned int writebuffer_chunksize; + + // FTDI FT2232C requirecments + int interface; // 0 or 1 + int index; // 1 or 2 + // Endpoints + int in_ep; + int out_ep; // 1 or 2 + + /* 1: (default) Normal bitbang mode, 2: FT2232C SPI bitbang mode */ + unsigned char bitbang_mode; + + // misc + char *error_str; +}; + +struct ftdi_device_list { + struct ftdi_device_list *next; + struct usb_device *dev; +}; + +struct ftdi_eeprom { + int vendor_id; + int product_id; + + int self_powered; + int remote_wakeup; + int BM_type_chip; + + int in_is_isochronous; + int out_is_isochronous; + int suspend_pull_downs; + + int use_serial; + int change_usb_version; + int usb_version; + int max_power; + + char *manufacturer; + char *product; + char *serial; +}; + +#ifdef __cplusplus +extern "C" { +#endif + + int ftdi_init(struct ftdi_context *ftdi); + int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface interface); + + void ftdi_deinit(struct ftdi_context *ftdi); + void ftdi_set_usbdev (struct ftdi_context *ftdi, usb_dev_handle *usbdev); + + int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devlist, + int vendor, int product); + void ftdi_list_free(struct ftdi_device_list **devlist); + + int ftdi_usb_open(struct ftdi_context *ftdi, int vendor, int product); + int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product, + const char* description, const char* serial); + int ftdi_usb_open_dev(struct ftdi_context *ftdi, struct usb_device *dev); + + int ftdi_usb_close(struct ftdi_context *ftdi); + int ftdi_usb_reset(struct ftdi_context *ftdi); + int ftdi_usb_purge_buffers(struct ftdi_context *ftdi); + + int ftdi_set_baudrate(struct ftdi_context *ftdi, int baudrate); + int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits, + enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity); + + int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size); + int ftdi_read_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize); + int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize); + + int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size); + int ftdi_write_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize); + int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize); + + int ftdi_enable_bitbang(struct ftdi_context *ftdi, unsigned char bitmask); + int ftdi_disable_bitbang(struct ftdi_context *ftdi); + int ftdi_set_bitmode(struct ftdi_context *ftdi, unsigned char bitmask, unsigned char mode); + int ftdi_read_pins(struct ftdi_context *ftdi, unsigned char *pins); + + int ftdi_set_latency_timer(struct ftdi_context *ftdi, unsigned char latency); + int ftdi_get_latency_timer(struct ftdi_context *ftdi, unsigned char *latency); + + // init and build eeprom from ftdi_eeprom structure + void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom); + int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output); + + // "eeprom" needs to be valid 128 byte eeprom (generated by the eeprom generator) + // the checksum of the eeprom is valided + int ftdi_read_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom); + int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom); + int ftdi_erase_eeprom(struct ftdi_context *ftdi); + + char *ftdi_get_error_string(struct ftdi_context *ftdi); + + /* + * Flow control code adapted from Linux kernel sources + * by Lorenz Moesenlechner (lorenz@hcilab.org) and + * Matthias Kranz (matthias@hcilab.org) + * */ + + /** + * Set flowcontrol for ftdi chip + * \param ftdi device context of ftdi + * \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 + */ + int ftdi_setflowctrl(struct ftdi_context *ftdi, int flowctrl); + + /** + * Set dtr line + * \param ftdi device context of ftdi + * \param state state to set line to (1 or 0) + */ + int ftdi_setdtr(struct ftdi_context *ftdi, int state); + + /** + * Set rts line + * \param ftdi device context of ftdi + * \param state state to set line to (1 or 0) + */ + int ftdi_setrts(struct ftdi_context *ftdi, int state); + +#ifdef __cplusplus +} +#endif + +#endif /* __libftdi_h__ */ -- 1.7.10.4