blob: 7f138fb24e0bd020cc637f75654aef3b7d60dd14 [file] [log] [blame]
// 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.
// 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));
}