729 lines
16 KiB
C
729 lines
16 KiB
C
|
/* 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 receiving, 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;
|
||
|
}
|