/* *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)); }