| /* Copyright 2015 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * Based on Craig Heffner's version of Dec 27 2011, published on |
| * https://github.com/devttys0/libmpsse |
| */ |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #if LIBFTDI1 == 1 |
| #include <libftdi1/ftdi.h> |
| #else |
| #include <ftdi.h> |
| #endif |
| |
| #include "mpsse.h" |
| #include "support.h" |
| |
| /* FTDI interfaces */ |
| enum interface { |
| IFACE_ANY = INTERFACE_ANY, |
| IFACE_A = INTERFACE_A, |
| IFACE_B = INTERFACE_B, |
| IFACE_C = INTERFACE_C, |
| IFACE_D = INTERFACE_D |
| }; |
| |
| enum mpsse_commands { |
| INVALID_COMMAND = 0xAB, |
| ENABLE_ADAPTIVE_CLOCK = 0x96, |
| DISABLE_ADAPTIVE_CLOCK = 0x97, |
| ENABLE_3_PHASE_CLOCK = 0x8C, |
| DISABLE_3_PHASE_CLOCK = 0x8D, |
| TCK_X5 = 0x8A, |
| TCK_D5 = 0x8B, |
| CLOCK_N_CYCLES = 0x8E, |
| CLOCK_N8_CYCLES = 0x8F, |
| PULSE_CLOCK_IO_HIGH = 0x94, |
| PULSE_CLOCK_IO_LOW = 0x95, |
| CLOCK_N8_CYCLES_IO_HIGH = 0x9C, |
| CLOCK_N8_CYCLES_IO_LOW = 0x9D, |
| TRISTATE_IO = 0x9E, |
| }; |
| |
| /* Common clock rates */ |
| enum clock_rates { |
| ONE_HUNDRED_KHZ = 100000, |
| FOUR_HUNDRED_KHZ = 400000, |
| ONE_MHZ = 1000000, |
| TWO_MHZ = 2000000, |
| FIVE_MHZ = 5000000, |
| SIX_MHZ = 6000000, |
| TEN_MHZ = 10000000, |
| TWELVE_MHZ = 12000000, |
| FIFTEEN_MHZ = 15000000, |
| THIRTY_MHZ = 30000000, |
| SIXTY_MHZ = 60000000 |
| }; |
| |
| #define NULL_CONTEXT_ERROR_MSG "NULL MPSSE context pointer!" |
| #define SPI_TRANSFER_SIZE 512 |
| #define SPI_RW_SIZE (63 * 1024) |
| #define SETUP_DELAY 25000 |
| #define LATENCY_MS 2 |
| #define USB_TIMEOUT 120000 |
| #define CHUNK_SIZE 65535 |
| #define MAX_SETUP_COMMANDS 10 |
| |
| /* SK and CS are high, GPIO1 is reset on the FPGA hookup, all others low */ |
| #define DEFAULT_PORT (SK | CS | GPIO1) |
| /* SK/DO/CS and GPIOs are outputs, DI is an input */ |
| #define DEFAULT_TRIS (SK | DO | CS | GPIO0 | GPIO1 | GPIO2 | GPIO3) |
| |
| static struct vid_pid { |
| int vid; |
| int pid; |
| char *description; |
| int use_B; |
| } supported_devices[] = { |
| { |
| 0x0403, 0x6010, "FT2232 Future Technology Devices International, Ltd", |
| 1}, |
| { |
| 0x0403, 0x6011, "FT4232 Future Technology Devices International, Ltd"}, |
| { |
| 0x0403, 0x6014, |
| "FT232H Future Technology Devices International, Ltd"}, |
| /* These devices are based on FT2232 chips, but have not been tested. */ |
| { |
| 0x0403, 0x8878, "Bus Blaster v2 (channel A)"}, { |
| 0x0403, 0x8879, "Bus Blaster v2 (channel B)"}, { |
| 0x0403, 0xBDC8, "Turtelizer JTAG/RS232 Adapter A"}, { |
| 0x0403, 0xCFF8, "Amontec JTAGkey"}, { |
| 0x0403, 0x8A98, "TIAO Multi Protocol Adapter"}, { |
| 0x15BA, 0x0003, "Olimex Ltd. OpenOCD JTAG"}, { |
| 0x15BA, 0x0004, "Olimex Ltd. OpenOCD JTAG TINY"}, { |
| 0x18d1, 0x0304, "Google UltraDebug", 1}, { |
| 0, 0, NULL} |
| }; |
| |
| /* |
| * Enables or disables flushing of the FTDI chip's RX buffers after each read |
| * operation. Flushing is disable by default. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @tf - Set to 1 to enable flushing, or 0 to disable flushing. |
| * |
| * Returns void. |
| */ |
| static void FlushAfterRead(struct mpsse_context *mpsse, int tf) |
| { |
| mpsse->flush_after_read = tf; |
| } |
| |
| /* |
| * Enable / disable internal loopback. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @enable - Zero to disable loopback, 1 to enable loopback. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| static int SetLoopback(struct mpsse_context *mpsse, int enable) |
| { |
| unsigned char buf[1] = { 0 }; |
| int retval = MPSSE_FAIL; |
| |
| if (is_valid_context(mpsse)) { |
| if (enable) |
| buf[0] = LOOPBACK_START; |
| else |
| buf[0] = LOOPBACK_END; |
| |
| retval = raw_write(mpsse, buf, 1); |
| } |
| |
| return retval; |
| } |
| |
| /* |
| * Sets the appropriate divisor for the desired clock frequency. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @freq - Desired clock frequency in hertz. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| static int SetClock(struct mpsse_context *mpsse, uint32_t freq) |
| { |
| int retval = MPSSE_FAIL; |
| uint32_t system_clock = 0; |
| uint16_t divisor = 0; |
| unsigned char buf[CMD_SIZE] = { 0 }; |
| |
| /* |
| * Do not call is_valid_context() here, as the FTDI chip may not be |
| * completely configured when SetClock is called |
| */ |
| if (!mpsse) |
| return retval; |
| |
| if (freq > SIX_MHZ) { |
| buf[0] = TCK_X5; |
| system_clock = SIXTY_MHZ; |
| } else { |
| buf[0] = TCK_D5; |
| system_clock = TWELVE_MHZ; |
| } |
| |
| if (raw_write(mpsse, buf, 1) == MPSSE_OK) { |
| if (freq <= 0) |
| divisor = 0xFFFF; |
| else |
| divisor = freq2div(system_clock, freq); |
| |
| buf[0] = TCK_DIVISOR; |
| buf[1] = (divisor & 0xFF); |
| buf[2] = ((divisor >> 8) & 0xFF); |
| |
| if (raw_write(mpsse, buf, 3) == MPSSE_OK) { |
| mpsse->clock = div2freq(system_clock, divisor); |
| retval = MPSSE_OK; |
| } |
| } |
| |
| return retval; |
| } |
| |
| /* |
| * Sets the appropriate transmit and receive commands based on the requested |
| * mode and byte order. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @endianness - MPSSE_MSB or MPSSE_LSB. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| static int SetMode(struct mpsse_context *mpsse, int endianness) |
| { |
| int retval = MPSSE_OK, i = 0, setup_commands_size = 0; |
| unsigned char buf[CMD_SIZE] = { 0 }; |
| unsigned char setup_commands[CMD_SIZE * MAX_SETUP_COMMANDS] = { 0 }; |
| |
| /* |
| * Do not call is_valid_context() here, as the FTDI chip may not be |
| * completely configured when SetMode is called |
| */ |
| if (!mpsse) |
| return MPSSE_FAIL; |
| |
| /* Read and write commands need to include endianness */ |
| mpsse->tx = MPSSE_DO_WRITE | endianness; |
| mpsse->rx = MPSSE_DO_READ | endianness; |
| mpsse->txrx = MPSSE_DO_WRITE | MPSSE_DO_READ | endianness; |
| |
| /* |
| * Clock, data out, chip select pins are outputs; all others are |
| * inputs. |
| */ |
| mpsse->tris = DEFAULT_TRIS; |
| |
| /* Clock and chip select pins idle high; all others are low */ |
| mpsse->pidle = mpsse->pstart = mpsse->pstop = DEFAULT_PORT; |
| |
| /* During reads and writes the chip select pin is brought low */ |
| mpsse->pstart &= ~CS; |
| |
| /* Disable FTDI internal loopback */ |
| SetLoopback(mpsse, 0); |
| |
| /* Ensure adaptive clock is disabled */ |
| setup_commands[setup_commands_size++] = DISABLE_ADAPTIVE_CLOCK; |
| |
| switch (mpsse->mode) { |
| case SPI0: |
| /* SPI mode 0 clock idles low */ |
| mpsse->pidle &= ~SK; |
| mpsse->pstart &= ~SK; |
| mpsse->pstop &= ~SK; |
| |
| /* |
| * SPI mode 0 propogates data on the falling edge and read |
| * data on the rising edge of the clock |
| */ |
| mpsse->tx |= MPSSE_WRITE_NEG; |
| mpsse->rx &= ~MPSSE_READ_NEG; |
| mpsse->txrx |= MPSSE_WRITE_NEG; |
| mpsse->txrx &= ~MPSSE_READ_NEG; |
| break; |
| default: |
| fprintf(stderr, "%s:%d attempt to set an unsupported mode %d\n", |
| __func__, __LINE__, mpsse->mode); |
| retval = MPSSE_FAIL; |
| } |
| |
| /* Send any setup commands to the chip */ |
| if ((retval == MPSSE_OK) && (setup_commands_size > 0)) |
| retval = raw_write(mpsse, setup_commands, setup_commands_size); |
| |
| if (retval == MPSSE_OK) { |
| /* Set the idle pin states */ |
| set_bits_low(mpsse, mpsse->pidle); |
| |
| /* All GPIO pins are outputs, set low */ |
| mpsse->trish = 0xFF; |
| mpsse->gpioh = 0x00; |
| |
| buf[i++] = SET_BITS_HIGH; |
| buf[i++] = mpsse->gpioh; |
| buf[i++] = mpsse->trish; |
| |
| retval = raw_write(mpsse, buf, i); |
| } |
| |
| return retval; |
| } |
| |
| /* |
| * Open device by VID/PID/index |
| * |
| * @vid - Device vendor ID. |
| * @pid - Device product ID. |
| * @freq - Clock frequency to use for the specified mode. |
| * @endianness - Specifies how data is clocked in/out (MSB, LSB). |
| * @interface - FTDI interface to use (IFACE_A - IFACE_D). |
| * @description - Device product description (set to NULL if not needed). |
| * @serial - Device serial number (set to NULL if not needed). |
| * @index - Device index (set to 0 if not needed). |
| * |
| * Returns a pointer to an MPSSE context structure. |
| * On success, mpsse->open will be set to 1. |
| * On failure, mpsse->open will be set to 0. |
| */ |
| static struct mpsse_context *OpenIndex(int vid, |
| int pid, |
| int freq, |
| int endianness, |
| int interface, |
| const char *description, |
| const char *serial, int index) |
| { |
| int status = 0; |
| struct mpsse_context *mpsse = NULL; |
| enum modes mode = SPI0; /* Let's use this mode at all times. */ |
| |
| mpsse = malloc(sizeof(struct mpsse_context)); |
| if (!mpsse) |
| return NULL; |
| |
| memset(mpsse, 0, sizeof(struct mpsse_context)); |
| |
| /* Legacy; flushing is no longer needed, so disable it by default. */ |
| FlushAfterRead(mpsse, 0); |
| |
| /* ftdilib initialization */ |
| if (ftdi_init(&mpsse->ftdi)) { |
| fprintf(stderr, "%s:%d failed to initialize FTDI\n", |
| __func__, __LINE__); |
| free(mpsse); |
| return NULL; |
| } |
| |
| mpsse->ftdi_initialized = 1; |
| |
| /* Set the FTDI interface */ |
| ftdi_set_interface(&mpsse->ftdi, interface); |
| |
| /* Try opening the specified device */ |
| if (ftdi_usb_open_desc_index |
| (&mpsse->ftdi, vid, pid, description, serial, index)) { |
| Close(mpsse); |
| return NULL; |
| } |
| |
| mpsse->mode = mode; |
| mpsse->vid = vid; |
| mpsse->pid = pid; |
| mpsse->status = STOPPED; |
| mpsse->endianness = endianness; |
| mpsse->xsize = SPI_RW_SIZE; |
| |
| status |= ftdi_usb_reset(&mpsse->ftdi); |
| status |= ftdi_set_latency_timer(&mpsse->ftdi, LATENCY_MS); |
| status |= ftdi_write_data_set_chunksize(&mpsse->ftdi, CHUNK_SIZE); |
| status |= ftdi_read_data_set_chunksize(&mpsse->ftdi, CHUNK_SIZE); |
| status |= ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_RESET); |
| |
| if (status) { |
| fprintf(stderr, |
| "%s:%d failed setting basic config for %4.4x:%4.4x\n", |
| __func__, __LINE__, vid, pid); |
| Close(mpsse); |
| return NULL; |
| } |
| /* Set the read and write timeout periods */ |
| set_timeouts(mpsse, USB_TIMEOUT); |
| |
| ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_MPSSE); |
| |
| if ((SetClock(mpsse, freq) != MPSSE_OK) |
| || (SetMode(mpsse, endianness) != MPSSE_OK)) { |
| fprintf(stderr, |
| "%s:%d failed setting clock/mode for %4.4x:%4.4x\n", |
| __func__, __LINE__, vid, pid); |
| Close(mpsse); |
| return NULL; |
| } |
| |
| mpsse->open = 1; |
| |
| /* Give the chip a few mS to initialize */ |
| usleep(SETUP_DELAY); |
| |
| /* |
| * Not all FTDI chips support all the commands that SetMode may have |
| * sent. This clears out any errors from unsupported commands that |
| * might have been sent during set up. |
| */ |
| ftdi_usb_purge_buffers(&mpsse->ftdi); |
| |
| return mpsse; |
| } |
| |
| /* |
| * Opens and initializes the first FTDI device found. |
| * |
| * @freq - Clock frequency to use for the specified mode. |
| * @endianness - Specifies how data is clocked in/out (MSB, LSB). |
| * @serial - Serial number of the USB device (NULL if not needed). |
| * |
| * Returns a pointer to an MPSSE context structure. |
| * On success, mpsse->open will be set to 1. |
| * On failure, mpsse->open will be set to 0. |
| */ |
| struct mpsse_context *MPSSE(int freq, int endianness, const char *serial) |
| { |
| int i = 0; |
| struct mpsse_context *mpsse = NULL; |
| |
| for (i = 0; supported_devices[i].vid != 0; i++) { |
| mpsse = OpenIndex(supported_devices[i].vid, |
| supported_devices[i].pid, freq, endianness, |
| supported_devices[i].use_B ? |
| IFACE_B : IFACE_A, |
| NULL, serial, 0); |
| if (!mpsse) |
| continue; |
| |
| if (mpsse->open) { |
| mpsse->description = supported_devices[i].description; |
| break; |
| } |
| /* |
| * If there is another device still left to try, free |
| * the context pointer and try again |
| */ |
| if (supported_devices[i + 1].vid != 0) { |
| Close(mpsse); |
| mpsse = NULL; |
| } |
| } |
| |
| return mpsse; |
| } |
| |
| /* |
| * Closes the device, deinitializes libftdi, and frees the MPSSE context |
| * pointer. |
| * |
| * @mpsse - MPSSE context pointer. |
| * |
| * Returns void. |
| */ |
| |
| void Close(struct mpsse_context *mpsse) |
| { |
| if (!mpsse) |
| return; |
| |
| if (mpsse->open) { |
| ftdi_usb_close(&mpsse->ftdi); |
| ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_RESET); |
| } |
| |
| if (mpsse->ftdi_initialized) |
| ftdi_deinit(&mpsse->ftdi); |
| |
| free(mpsse); |
| } |
| |
| /* |
| * Retrieves the last error string from libftdi. |
| * |
| * @mpsse - MPSSE context pointer. |
| * |
| * Returns a pointer to the last error string. |
| */ |
| const char *ErrorString(struct mpsse_context *mpsse) |
| { |
| if (mpsse) |
| return ftdi_get_error_string(&mpsse->ftdi); |
| |
| return NULL_CONTEXT_ERROR_MSG; |
| } |
| |
| /* |
| * Send data start condition. |
| * |
| * @mpsse - MPSSE context pointer. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| int Start(struct mpsse_context *mpsse) |
| { |
| int status; |
| |
| if (!is_valid_context(mpsse)) { |
| mpsse->status = STOPPED; |
| return MPSSE_FAIL; |
| } |
| |
| /* Set the start condition */ |
| status = set_bits_low(mpsse, mpsse->pstart); |
| |
| if (status == MPSSE_OK) |
| mpsse->status = STARTED; |
| |
| return status; |
| } |
| |
| /* |
| * Send data out via the selected serial protocol. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @data - Buffer of data to send. |
| * @size - Size of data. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| int Write(struct mpsse_context *mpsse, char *data, int size) |
| { |
| int n = 0; |
| |
| if (!is_valid_context(mpsse)) |
| return MPSSE_FAIL; |
| |
| if (!mpsse->mode) |
| return MPSSE_FAIL; |
| |
| while (n < size) { |
| unsigned char *buf; |
| int retval, buf_size, txsize; |
| |
| txsize = size - n; |
| if (txsize > mpsse->xsize) |
| txsize = mpsse->xsize; |
| |
| buf = build_block_buffer(mpsse, mpsse->tx, |
| (unsigned char *)(data + n), |
| txsize, &buf_size); |
| if (!buf) |
| return MPSSE_FAIL; |
| |
| retval = raw_write(mpsse, buf, buf_size); |
| n += txsize; |
| free(buf); |
| |
| if (retval != MPSSE_OK) |
| return retval; |
| |
| } |
| |
| return MPSSE_OK; |
| } |
| |
| /* Performs a read. For internal use only; see Read() and ReadBits(). */ |
| static char *InternalRead(struct mpsse_context *mpsse, int size) |
| { |
| unsigned char *buf; |
| int n = 0; |
| |
| if (!is_valid_context(mpsse)) |
| return NULL; |
| |
| if (!mpsse->mode) |
| return NULL; |
| buf = malloc(size); |
| |
| if (!buf) |
| return NULL; |
| |
| while (n < size) { |
| int rxsize, data_size, retval; |
| unsigned char *data; |
| unsigned char sbuf[SPI_RW_SIZE] = { 0 }; |
| |
| rxsize = size - n; |
| if (rxsize > mpsse->xsize) |
| rxsize = mpsse->xsize; |
| |
| data = build_block_buffer(mpsse, mpsse->rx, |
| sbuf, rxsize, &data_size); |
| if (!data) { |
| free(buf); |
| return NULL; |
| } |
| |
| retval = raw_write(mpsse, data, data_size); |
| free(data); |
| |
| if (retval != MPSSE_OK) { |
| free(buf); |
| return NULL; |
| } |
| n += raw_read(mpsse, buf + n, rxsize); |
| } |
| |
| return (char *)buf; |
| } |
| |
| /* |
| * Reads data over the selected serial protocol. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @size - Number of bytes to read. |
| * |
| * Returns a pointer to the read data on success. |
| * Returns NULL on failure. |
| */ |
| char *Read(struct mpsse_context *mpsse, int size) |
| { |
| char *buf = NULL; |
| |
| buf = InternalRead(mpsse, size); |
| return buf; |
| } |
| |
| /* |
| * Reads and writes data over the selected serial protocol (SPI only). |
| * |
| * @mpsse - MPSSE context pointer. |
| * @data - Buffer containing bytes to write. |
| * @size - Number of bytes to transfer. |
| * |
| * Returns a pointer to the read data on success. |
| * Returns NULL on failure. |
| */ |
| char *Transfer(struct mpsse_context *mpsse, char *data, int size) |
| { |
| unsigned char *txdata = NULL, *buf = NULL; |
| int n = 0, data_size = 0, rxsize = 0, retval = MPSSE_OK; |
| |
| if (!is_valid_context(mpsse)) |
| return NULL; |
| |
| buf = malloc(size); |
| if (!buf) |
| return NULL; |
| |
| while (n < size) { |
| /* |
| * When sending and recieving, FTDI chips don't seem to like |
| * large data blocks. Limit the size of each block to |
| * SPI_TRANSFER_SIZE |
| */ |
| rxsize = size - n; |
| if (rxsize > SPI_TRANSFER_SIZE) |
| rxsize = SPI_TRANSFER_SIZE; |
| |
| txdata = build_block_buffer(mpsse, mpsse->txrx, |
| (unsigned char *)(data + n), |
| rxsize, &data_size); |
| if (!txdata) { |
| retval = MPSSE_FAIL; |
| break; |
| } |
| retval = raw_write(mpsse, txdata, data_size); |
| free(txdata); |
| |
| if (retval != MPSSE_OK) |
| break; |
| |
| n += raw_read(mpsse, (buf + n), rxsize); |
| } |
| |
| if (retval != MPSSE_OK) |
| return NULL; |
| |
| return (char *)buf; |
| } |
| |
| /* |
| * Send data stop condition. |
| * |
| * @mpsse - MPSSE context pointer. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| int Stop(struct mpsse_context *mpsse) |
| { |
| int retval = MPSSE_OK; |
| |
| if (is_valid_context(mpsse)) { |
| /* Send the stop condition */ |
| retval |= set_bits_low(mpsse, mpsse->pstop); |
| |
| if (retval == MPSSE_OK) { |
| /* Restore the pins to their idle states */ |
| retval |= set_bits_low(mpsse, mpsse->pidle); |
| } |
| |
| mpsse->status = STOPPED; |
| } else { |
| retval = MPSSE_FAIL; |
| mpsse->status = STOPPED; |
| } |
| |
| return retval; |
| } |
| |
| /* |
| * Sets the specified pin high. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @pin - Pin number to set high. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| int PinHigh(struct mpsse_context *mpsse, int pin) |
| { |
| int retval = MPSSE_FAIL; |
| |
| if (is_valid_context(mpsse)) |
| retval = gpio_write(mpsse, pin, HIGH); |
| |
| return retval; |
| } |
| |
| /* |
| * Sets the specified pin low. |
| * |
| * @mpsse - MPSSE context pointer. |
| * @pin - Pin number to set low. |
| * |
| * Returns MPSSE_OK on success. |
| * Returns MPSSE_FAIL on failure. |
| */ |
| int PinLow(struct mpsse_context *mpsse, int pin) |
| { |
| int retval = MPSSE_FAIL; |
| |
| if (is_valid_context(mpsse)) |
| retval = gpio_write(mpsse, pin, LOW); |
| |
| return retval; |
| } |