You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1150 lines
30 KiB
1150 lines
30 KiB
/* |
|
*Copyright (C) 2015 The Android Open Source Project |
|
* |
|
*Licensed under the Apache License, Version 2.0 (the "License"); |
|
*you may not use this file except in compliance with the License. |
|
*You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
*Unless required by applicable law or agreed to in writing, software |
|
*distributed under the License is distributed on an "AS IS" BASIS, |
|
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
*See the License for the specific language governing permissions and |
|
*limitations under the License. |
|
* |
|
* This file was copied from https://github.com/devttys0/libmpsse.git (sha1 |
|
* f1a6744b), and modified to suite the Chromium OS project. |
|
* |
|
* Main libmpsse source file. |
|
* |
|
* Craig Heffner |
|
* 27 December 2011 |
|
*/ |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdint.h> |
|
#include <unistd.h> |
|
|
|
#include "trunks/ftdi/support.h" |
|
|
|
/* List of known FT2232-based devices */ |
|
struct vid_pid supported_devices[] = { |
|
{0x0403, 0x6010, "FT2232 Future Technology Devices International, Ltd"}, |
|
{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"}, |
|
|
|
{0, 0, NULL}}; |
|
|
|
/* |
|
* Opens and initializes the first FTDI device found. |
|
* |
|
* @mode - Mode to open the device in. One of enum modes. |
|
* @freq - Clock frequency to use for the specified mode. |
|
* @endianess - Specifies how data is clocked in/out (MSB, LSB). |
|
* |
|
* Returns a pointer to an MPSSE context structure if succeeded, NULL otherwise. |
|
*/ |
|
struct mpsse_context* MPSSE(enum modes mode, int freq, int endianess) { |
|
int i = 0; |
|
struct mpsse_context* mpsse = NULL; |
|
|
|
for (i = 0; supported_devices[i].vid != 0; i++) { |
|
mpsse = Open(supported_devices[i].vid, supported_devices[i].pid, mode, freq, |
|
endianess, IFACE_A, NULL, NULL); |
|
if (mpsse) { |
|
mpsse->description = supported_devices[i].description; |
|
return mpsse; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/* |
|
* Open device by VID/PID |
|
* |
|
* @vid - Device vendor ID. |
|
* @pid - Device product ID. |
|
* @mode - MPSSE mode, one of enum modes. |
|
* @freq - Clock frequency to use for the specified mode. |
|
* @endianess - 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). |
|
* |
|
* Returns a pointer to an MPSSE context structure on success. |
|
*/ |
|
struct mpsse_context* Open(int vid, |
|
int pid, |
|
enum modes mode, |
|
int freq, |
|
int endianess, |
|
int interface, |
|
const char* description, |
|
const char* serial) { |
|
return OpenIndex(vid, pid, mode, freq, endianess, interface, description, |
|
serial, 0); |
|
} |
|
|
|
/* |
|
* Open device by VID/PID/index |
|
* |
|
* @vid - Device vendor ID. |
|
* @pid - Device product ID. |
|
* @mode - MPSSE mode, one of enum modes. |
|
* @freq - Clock frequency to use for the specified mode. |
|
* @endianess - 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. |
|
*/ |
|
struct mpsse_context* OpenIndex(int vid, |
|
int pid, |
|
enum modes mode, |
|
int freq, |
|
int endianess, |
|
int interface, |
|
const char* description, |
|
const char* serial, |
|
int index) { |
|
int status = 0; |
|
struct mpsse_context* mpsse = NULL; |
|
|
|
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)) { |
|
free(mpsse); |
|
return NULL; |
|
} |
|
|
|
/* Set the FTDI interface */ |
|
ftdi_set_interface(&mpsse->ftdi, interface); |
|
|
|
/* Open the specified device */ |
|
if (!ftdi_usb_open_desc_index(&mpsse->ftdi, vid, pid, description, serial, |
|
index)) { |
|
mpsse->mode = mode; |
|
mpsse->vid = vid; |
|
mpsse->pid = pid; |
|
mpsse->status = STOPPED; |
|
mpsse->endianess = endianess; |
|
|
|
/* Set the appropriate transfer size for the requested protocol */ |
|
if (mpsse->mode == I2C) |
|
mpsse->xsize = I2C_TRANSFER_SIZE; |
|
else |
|
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 == 0) { |
|
/* Set the read and write timeout periods */ |
|
set_timeouts(mpsse, USB_TIMEOUT); |
|
|
|
if (mpsse->mode != BITBANG) { |
|
ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_MPSSE); |
|
|
|
if (SetClock(mpsse, freq) == MPSSE_OK) { |
|
if (SetMode(mpsse, endianess) == MPSSE_OK) { |
|
mpsse->opened = 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); |
|
} |
|
} |
|
} else { |
|
/* Skip the setup functions if we're just operating in BITBANG mode |
|
*/ |
|
if (!ftdi_set_bitmode(&mpsse->ftdi, 0xFF, BITMODE_BITBANG)) |
|
mpsse->opened = 1; |
|
} |
|
} |
|
} |
|
|
|
if (mpsse && !mpsse->opened) { |
|
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->opened) { |
|
/* Shut these down only if initialization succeeded before. */ |
|
ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_RESET); |
|
ftdi_usb_close(&mpsse->ftdi); |
|
} |
|
ftdi_deinit(&mpsse->ftdi); |
|
free(mpsse); |
|
} |
|
|
|
/* Enables bit-wise data transfers. |
|
* Must be called after MPSSE() / Open() / OpenIndex(). |
|
* |
|
* Returns void. |
|
*/ |
|
void EnableBitmode(struct mpsse_context* mpsse, int tf) { |
|
if (is_valid_context(mpsse)) { |
|
if (tf) { |
|
mpsse->tx |= MPSSE_BITMODE; |
|
mpsse->rx |= MPSSE_BITMODE; |
|
mpsse->txrx |= MPSSE_BITMODE; |
|
} else { |
|
mpsse->tx &= ~MPSSE_BITMODE; |
|
mpsse->rx &= ~MPSSE_BITMODE; |
|
mpsse->txrx &= ~MPSSE_BITMODE; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* Sets the appropriate transmit and receive commands based on the requested |
|
*mode and byte order. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @endianess - MPSSE_MSB or MPSSE_LSB. |
|
* |
|
* Returns MPSSE_OK on success. |
|
* Returns MPSSE_FAIL on failure. |
|
*/ |
|
int SetMode(struct mpsse_context* mpsse, int endianess) { |
|
int retval = MPSSE_OK, i = 0, setup_commands_size = 0; |
|
uint8_t buf[CMD_SIZE] = {0}; |
|
uint8_t 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) { |
|
/* Read and write commands need to include endianess */ |
|
mpsse->tx = MPSSE_DO_WRITE | endianess; |
|
mpsse->rx = MPSSE_DO_READ | endianess; |
|
mpsse->txrx = MPSSE_DO_WRITE | MPSSE_DO_READ | endianess; |
|
|
|
/* 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); |
|
|
|
/* Send ACKs by default */ |
|
SetAck(mpsse, ACK); |
|
|
|
/* 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; |
|
case SPI3: |
|
/* SPI mode 3 clock idles high */ |
|
mpsse->pidle |= SK; |
|
mpsse->pstart |= SK; |
|
/* Keep the clock low while the CS pin is brought high to ensure we |
|
* don't accidentally clock out an extra bit */ |
|
mpsse->pstop &= ~SK; |
|
/* SPI mode 3 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; |
|
case SPI1: |
|
/* SPI mode 1 clock idles low */ |
|
mpsse->pidle &= ~SK; |
|
/* Since this mode idles low, the start condition should ensure that the |
|
* clock is low */ |
|
mpsse->pstart &= ~SK; |
|
/* Even though we idle low in this mode, we need to keep the clock line |
|
* high when we set the CS pin high to prevent |
|
* an unintended clock cycle from being sent by the FT2232. This way, |
|
* the clock goes high, but does not go low until |
|
* after the CS pin goes high. |
|
*/ |
|
mpsse->pstop |= SK; |
|
/* Data read on falling clock edge */ |
|
mpsse->rx |= MPSSE_READ_NEG; |
|
mpsse->tx &= ~MPSSE_WRITE_NEG; |
|
mpsse->txrx |= MPSSE_READ_NEG; |
|
mpsse->txrx &= ~MPSSE_WRITE_NEG; |
|
break; |
|
case SPI2: |
|
/* SPI 2 clock idles high */ |
|
mpsse->pidle |= SK; |
|
mpsse->pstart |= SK; |
|
mpsse->pstop |= SK; |
|
/* Data read on falling clock edge */ |
|
mpsse->rx |= MPSSE_READ_NEG; |
|
mpsse->tx &= ~MPSSE_WRITE_NEG; |
|
mpsse->txrx |= MPSSE_READ_NEG; |
|
mpsse->txrx &= ~MPSSE_WRITE_NEG; |
|
break; |
|
case I2C: |
|
/* I2C propogates data on the falling clock edge and reads data on the |
|
* falling (or rising) clock edge */ |
|
mpsse->tx |= MPSSE_WRITE_NEG; |
|
mpsse->rx &= ~MPSSE_READ_NEG; |
|
/* In I2C, both the clock and the data lines idle high */ |
|
mpsse->pidle |= DO | DI; |
|
/* I2C start bit == data line goes from high to low while clock line is |
|
* high */ |
|
mpsse->pstart &= ~DO & ~DI; |
|
/* I2C stop bit == data line goes from low to high while clock line is |
|
* high - set data line low here, so the transition to the idle state |
|
* triggers the stop condition. */ |
|
mpsse->pstop &= ~DO & ~DI; |
|
/* Enable three phase clock to ensure that I2C data is available on both |
|
* the rising and falling clock edges */ |
|
setup_commands[setup_commands_size++] = ENABLE_3_PHASE_CLOCK; |
|
break; |
|
case GPIO: |
|
break; |
|
default: |
|
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); |
|
} |
|
} else { |
|
retval = MPSSE_FAIL; |
|
} |
|
|
|
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. |
|
*/ |
|
int SetClock(struct mpsse_context* mpsse, uint32_t freq) { |
|
int retval = MPSSE_FAIL; |
|
uint32_t system_clock = 0; |
|
uint16_t divisor = 0; |
|
uint8_t 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) { |
|
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; |
|
} |
|
|
|
/* |
|
* 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 != NULL) { |
|
return ftdi_get_error_string(&mpsse->ftdi); |
|
} |
|
|
|
return NULL_CONTEXT_ERROR_MSG; |
|
} |
|
|
|
/* |
|
* Gets the currently configured clock rate. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns the existing clock rate in hertz. |
|
*/ |
|
int GetClock(struct mpsse_context* mpsse) { |
|
int clock = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
clock = mpsse->clock; |
|
} |
|
|
|
return clock; |
|
} |
|
|
|
/* |
|
* Returns the vendor ID of the FTDI chip. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns the integer value of the vendor ID. |
|
*/ |
|
int GetVid(struct mpsse_context* mpsse) { |
|
int vid = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
vid = mpsse->vid; |
|
} |
|
|
|
return vid; |
|
} |
|
|
|
/* |
|
* Returns the product ID of the FTDI chip. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns the integer value of the product ID. |
|
*/ |
|
int GetPid(struct mpsse_context* mpsse) { |
|
int pid = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
pid = mpsse->pid; |
|
} |
|
|
|
return pid; |
|
} |
|
|
|
/* |
|
* Returns the description of the FTDI chip, if any. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns the description of the FTDI chip. |
|
*/ |
|
const char* GetDescription(struct mpsse_context* mpsse) { |
|
char* description = NULL; |
|
|
|
if (is_valid_context(mpsse)) { |
|
description = mpsse->description; |
|
} |
|
|
|
return description; |
|
} |
|
|
|
/* |
|
* 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. |
|
*/ |
|
int SetLoopback(struct mpsse_context* mpsse, int enable) { |
|
uint8_t 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 idle state of the chip select pin. CS idles high by default. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @idle - Set to 1 to idle high, 0 to idle low. |
|
* |
|
* Returns void. |
|
*/ |
|
void SetCSIdle(struct mpsse_context* mpsse, int idle) { |
|
if (is_valid_context(mpsse)) { |
|
if (idle > 0) { |
|
/* Chip select idles high, active low */ |
|
mpsse->pidle |= CS; |
|
mpsse->pstop |= CS; |
|
mpsse->pstart &= ~CS; |
|
} else { |
|
/* Chip select idles low, active high */ |
|
mpsse->pidle &= ~CS; |
|
mpsse->pstop &= ~CS; |
|
mpsse->pstart |= CS; |
|
} |
|
} |
|
|
|
return; |
|
} |
|
|
|
/* |
|
* 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. |
|
*/ |
|
void FlushAfterRead(struct mpsse_context* mpsse, int tf) { |
|
mpsse->flush_after_read = tf; |
|
return; |
|
} |
|
|
|
/* |
|
* 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 = MPSSE_OK; |
|
|
|
if (is_valid_context(mpsse)) { |
|
if (mpsse->mode == I2C && mpsse->status == STARTED) { |
|
/* Set the default pin states while the clock is low since this is an I2C |
|
* repeated start condition */ |
|
status |= set_bits_low(mpsse, (mpsse->pidle & ~SK)); |
|
|
|
/* Make sure the pins are in their default idle state */ |
|
status |= set_bits_low(mpsse, mpsse->pidle); |
|
} |
|
|
|
/* Set the start condition */ |
|
status |= set_bits_low(mpsse, mpsse->pstart); |
|
|
|
/* |
|
* Hackish work around to properly support SPI mode 3. |
|
* SPI3 clock idles high, but needs to be set low before sending out |
|
* data to prevent unintenteded clock glitches from the FT2232. |
|
*/ |
|
if (mpsse->mode == SPI3) { |
|
status |= set_bits_low(mpsse, (mpsse->pstart & ~SK)); |
|
} |
|
/* |
|
* Hackish work around to properly support SPI mode 1. |
|
* SPI1 clock idles low, but needs to be set high before sending out |
|
* data to preven unintended clock glitches from the FT2232. |
|
*/ |
|
else if (mpsse->mode == SPI1) { |
|
status |= set_bits_low(mpsse, (mpsse->pstart | SK)); |
|
} |
|
|
|
mpsse->status = STARTED; |
|
} else { |
|
status = MPSSE_FAIL; |
|
mpsse->status = STOPPED; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
/* |
|
* Performs a bit-wise write of up to 8 bits at a time. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @bits - A byte containing the desired bits to write. |
|
* @size - The number of bits from the 'bits' byte to write. |
|
* |
|
* Returns MPSSE_OK on success, MPSSE_FAIL on failure. |
|
*/ |
|
int WriteBits(struct mpsse_context* mpsse, char bits, size_t size) { |
|
uint8_t data[8] = {0}; |
|
size_t i = 0; |
|
int retval = MPSSE_OK; |
|
|
|
if (size > sizeof(data)) { |
|
size = sizeof(data); |
|
} |
|
|
|
/* Convert each bit in bits to an array of bytes */ |
|
for (i = 0; i < size; i++) { |
|
if (bits & (1 << i)) { |
|
/* Be sure to honor endianess */ |
|
if (mpsse->endianess == LSB) { |
|
data[i] = '\xFF'; |
|
} else { |
|
data[size - i - 1] = '\xFF'; |
|
} |
|
} |
|
} |
|
|
|
/* Enable bit mode before writing, then disable it afterwards. */ |
|
EnableBitmode(mpsse, 1); |
|
retval = Write(mpsse, data, size); |
|
EnableBitmode(mpsse, 0); |
|
|
|
return retval; |
|
} |
|
|
|
/* |
|
* 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, const void* vdata, int size) { |
|
const uint8_t* data = vdata; |
|
uint8_t* buf = NULL; |
|
int retval = MPSSE_FAIL, buf_size = 0, txsize = 0, n = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
if (mpsse->mode) { |
|
while (n < size) { |
|
txsize = size - n; |
|
if (txsize > mpsse->xsize) { |
|
txsize = mpsse->xsize; |
|
} |
|
|
|
/* |
|
* For I2C we need to send each byte individually so that we can |
|
* read back each individual ACK bit, so set the transmit size to 1. |
|
*/ |
|
if (mpsse->mode == I2C) { |
|
txsize = 1; |
|
} |
|
|
|
buf = build_block_buffer(mpsse, mpsse->tx, data + n, txsize, &buf_size); |
|
if (buf) { |
|
retval = raw_write(mpsse, buf, buf_size); |
|
n += txsize; |
|
free(buf); |
|
|
|
if (retval == MPSSE_FAIL) { |
|
break; |
|
} |
|
|
|
/* Read in the ACK bit and store it in mpsse->rack */ |
|
if (mpsse->mode == I2C) { |
|
raw_read(mpsse, (uint8_t*)&mpsse->rack, 1); |
|
} |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (retval == MPSSE_OK && n == size) { |
|
retval = MPSSE_OK; |
|
} |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/* Performs a read. For internal use only; see Read() and ReadBits(). */ |
|
static uint8_t* InternalRead(struct mpsse_context* mpsse, int size) { |
|
uint8_t *data = NULL, *buf = NULL; |
|
uint8_t sbuf[SPI_RW_SIZE] = {0}; |
|
int n = 0, rxsize = 0, data_size = 0, retval = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
if (mpsse->mode) { |
|
buf = malloc(size); |
|
if (buf) { |
|
memset(buf, 0, size); |
|
|
|
while (n < size) { |
|
rxsize = size - n; |
|
if (rxsize > mpsse->xsize) { |
|
rxsize = mpsse->xsize; |
|
} |
|
|
|
data = build_block_buffer(mpsse, mpsse->rx, sbuf, rxsize, &data_size); |
|
if (data) { |
|
retval = raw_write(mpsse, data, data_size); |
|
free(data); |
|
|
|
if (retval == MPSSE_OK) { |
|
n += raw_read(mpsse, buf + n, rxsize); |
|
} else { |
|
break; |
|
} |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
return 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. |
|
*/ |
|
#ifdef SWIGPYTHON |
|
swig_string_data Read(struct mpsse_context* mpsse, int size) |
|
#else |
|
uint8_t* Read(struct mpsse_context* mpsse, int size) |
|
#endif |
|
{ |
|
uint8_t* buf = NULL; |
|
|
|
buf = InternalRead(mpsse, size); |
|
|
|
#ifdef SWIGPYTHON |
|
swig_string_data sdata = {0}; |
|
sdata.size = size; |
|
sdata.data = buf; |
|
return sdata; |
|
#else |
|
return buf; |
|
#endif |
|
} |
|
|
|
/* |
|
* Performs a bit-wise read of up to 8 bits. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @size - Number of bits to read. |
|
* |
|
* Returns an 8-bit byte containing the read bits. |
|
*/ |
|
char ReadBits(struct mpsse_context* mpsse, int size) { |
|
char bits = 0; |
|
uint8_t* rdata = NULL; |
|
|
|
if (size > 8) { |
|
size = 8; |
|
} |
|
|
|
EnableBitmode(mpsse, 1); |
|
rdata = InternalRead(mpsse, size); |
|
EnableBitmode(mpsse, 0); |
|
|
|
if (rdata) { |
|
/* The last byte in rdata will have all the read bits set or unset as |
|
* needed. */ |
|
bits = rdata[size - 1]; |
|
|
|
if (mpsse->endianess == MSB) { |
|
/* |
|
* In MSB mode, bits are sifted in from the left. If less than 8 bits were |
|
* read, we need to shift them left accordingly. |
|
*/ |
|
bits = bits << (8 - size); |
|
} else if (mpsse->endianess == LSB) { |
|
/* |
|
* In LSB mode, bits are shifted in from the right. If less than 8 bits |
|
* were |
|
* read, we need to shift them right accordingly. |
|
*/ |
|
bits = bits >> (8 - size); |
|
} |
|
|
|
free(rdata); |
|
} |
|
|
|
return bits; |
|
} |
|
|
|
/* |
|
* 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. |
|
*/ |
|
#ifdef SWIGPYTHON |
|
swig_string_data Transfer(struct mpsse_context* mpsse, char* data, int size) |
|
#else |
|
uint8_t* Transfer(struct mpsse_context* mpsse, uint8_t* data, int size) |
|
#endif |
|
{ |
|
uint8_t *txdata = NULL, *buf = NULL; |
|
int n = 0, data_size = 0, rxsize = 0, retval = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
/* Make sure we're configured for one of the SPI modes */ |
|
if (mpsse->mode >= SPI0 && mpsse->mode <= SPI3) { |
|
buf = malloc(size); |
|
if (buf) { |
|
memset(buf, 0, size); |
|
|
|
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, data + n, rxsize, |
|
&data_size); |
|
if (txdata) { |
|
retval = raw_write(mpsse, txdata, data_size); |
|
free(txdata); |
|
|
|
if (retval == MPSSE_OK) { |
|
n += raw_read(mpsse, (buf + n), rxsize); |
|
} else { |
|
break; |
|
} |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
#ifdef SWIGPYTHON |
|
swig_string_data sdata = {0}; |
|
sdata.size = n; |
|
sdata.data = (char*)buf; |
|
return sdata; |
|
#else |
|
return buf; |
|
#endif |
|
} |
|
|
|
/* |
|
* Returns the last received ACK bit. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns either an ACK (0) or a NACK (1). |
|
*/ |
|
int GetAck(struct mpsse_context* mpsse) { |
|
int ack = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
ack = (mpsse->rack & 0x01); |
|
} |
|
|
|
return ack; |
|
} |
|
|
|
/* |
|
* Sets the transmitted ACK bit. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @ack - 0 to send ACKs, 1 to send NACKs. |
|
* |
|
* Returns void. |
|
*/ |
|
void SetAck(struct mpsse_context* mpsse, int ack) { |
|
if (is_valid_context(mpsse)) { |
|
if (ack == NACK) { |
|
mpsse->tack = 0xFF; |
|
} else { |
|
mpsse->tack = 0x00; |
|
} |
|
} |
|
|
|
return; |
|
} |
|
|
|
/* |
|
* Causes libmpsse to send ACKs after each read byte in I2C mode. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns void. |
|
*/ |
|
void SendAcks(struct mpsse_context* mpsse) { |
|
return SetAck(mpsse, ACK); |
|
} |
|
|
|
/* |
|
* Causes libmpsse to send NACKs after each read byte in I2C mode. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns void. |
|
*/ |
|
void SendNacks(struct mpsse_context* mpsse) { |
|
return SetAck(mpsse, NACK); |
|
} |
|
|
|
/* |
|
* 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)) { |
|
/* In I2C mode, we need to ensure that the data line goes low while the |
|
* clock line is low to avoid sending an inadvertent start condition */ |
|
if (mpsse->mode == I2C) { |
|
retval |= set_bits_low(mpsse, (mpsse->pidle & ~DO & ~SK)); |
|
} |
|
|
|
/* 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; |
|
} |
|
|
|
/* |
|
* Sets the input/output direction of all pins. For use in BITBANG mode only. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @direction - Byte indicating input/output direction of each bit. 1 is out. |
|
* |
|
* Returns MPSSE_OK if direction could be set, MPSSE_FAIL otherwise. |
|
*/ |
|
int SetDirection(struct mpsse_context* mpsse, uint8_t direction) { |
|
int retval = MPSSE_FAIL; |
|
|
|
if (is_valid_context(mpsse)) { |
|
if (mpsse->mode == BITBANG) { |
|
if (ftdi_set_bitmode(&mpsse->ftdi, direction, BITMODE_BITBANG) == 0) { |
|
retval = MPSSE_OK; |
|
} |
|
} |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/* |
|
* Sets the input/output value of all pins. For use in BITBANG mode only. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @data - Byte indicating bit hi/low value of each bit. |
|
* |
|
* Returns MPSSE_OK if direction could be set, MPSSE_FAIL otherwise. |
|
*/ |
|
int WritePins(struct mpsse_context* mpsse, uint8_t data) { |
|
int retval = MPSSE_FAIL; |
|
|
|
if (is_valid_context(mpsse)) { |
|
if (mpsse->mode == BITBANG) { |
|
if (ftdi_write_data(&mpsse->ftdi, &data, 1) == 0) { |
|
retval = MPSSE_OK; |
|
} |
|
} |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/* |
|
* Reads the state of the chip's pins. For use in BITBANG mode only. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns a byte with the corresponding pin's bits set to 1 or 0. |
|
*/ |
|
int ReadPins(struct mpsse_context* mpsse) { |
|
uint8_t val = 0; |
|
|
|
if (is_valid_context(mpsse)) { |
|
ftdi_read_pins((struct ftdi_context*)&mpsse->ftdi, (uint8_t*)&val); |
|
} |
|
|
|
return (int)val; |
|
} |
|
|
|
/* |
|
* Checks if a specific pin is high or low. For use in BITBANG mode only. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* @pin - The pin number. |
|
* @state - The state of the pins, as returned by ReadPins. |
|
* If set to -1, ReadPins will automatically be called. |
|
* |
|
* Returns a 1 if the pin is high, 0 if the pin is low. |
|
*/ |
|
int PinState(struct mpsse_context* mpsse, int pin, int state) { |
|
if (state == -1) { |
|
state = ReadPins(mpsse); |
|
} |
|
|
|
/* If not in bitbang mode, the specified pin should be one of GPIOLx. Convert |
|
* these defines into an absolute pin number. */ |
|
if (mpsse->mode != BITBANG) { |
|
pin += NUM_GPIOL_PINS; |
|
} |
|
|
|
return ((state & (1 << pin)) >> pin); |
|
} |
|
|
|
/* |
|
* Places all I/O pins into a tristate mode. |
|
* |
|
* @mpsse - MPSSE context pointer. |
|
* |
|
* Returns MPSSE_OK on success, MPSSE_FAIL on failure. |
|
*/ |
|
int Tristate(struct mpsse_context* mpsse) { |
|
uint8_t cmd[CMD_SIZE] = {0}; |
|
|
|
/* Tristate the all I/O pins (FT232H only) */ |
|
cmd[0] = TRISTATE_IO; |
|
cmd[1] = 0xFF; |
|
cmd[2] = 0xFF; |
|
|
|
return raw_write(mpsse, cmd, sizeof(cmd)); |
|
}
|
|
|