/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (c) 2023 Infineon Technologies AG
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE
 */
/*
 * This file was copied from https://github.com/devttys0/libmpsse.git (sha1: a2eafa2)
 * and modified accordingly.
 *
 * Copyright (c) 2015, Craig Heffner
 * All rights reserved.
 * SPDX short identifier: BSD-2-Clause
 */
#ifndef LIBMPSSE_H_
#define LIBMPSSE_H_

#include <stdint.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if LIBFTDI_VERSION == 1
#include <libftdi1/ftdi.h>
#else
#include <ftdi.h>
#endif

#define MPSSE_OK               0
#define MPSSE_FAIL             (-1)

#define MSB                    0x00
#define LSB                    0x08

#define CHUNK_SIZE             65535
#define SPI_RW_SIZE            (63 * 1024)
#define SPI_TRANSFER_SIZE      512
#define I2C_TRANSFER_SIZE      64

#define LATENCY_MS             2
#define TIMEOUT_DIVISOR        1000000
#define USB_TIMEOUT            120000
#define SETUP_DELAY            25000

#define BITMODE_RESET          0
#define BITMODE_MPSSE          2

#define CMD_SIZE               3
#define MAX_SETUP_COMMANDS     10
#define SS_TX_COUNT            3

#define LOW                    0
#define HIGH                   1
#define NUM_GPIOL_PINS         4
#define NUM_GPIO_PINS          12

#define NULL_CONTEXT_ERROR_MSG "NULL MPSSE context pointer!"

/* FTDI interfaces */
enum interface {
    IFACE_ANY = INTERFACE_ANY,
    IFACE_A = INTERFACE_A,
    IFACE_B = INTERFACE_B,
    IFACE_C = INTERFACE_C,
    IFACE_D = INTERFACE_D
};

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

/* Supported MPSSE modes */
enum modes {
    SPI0 = 1,
    SPI1 = 2,
    SPI2 = 3,
    SPI3 = 4,
    I2C = 5,
    GPIO = 6,
    BITBANG = 7,
};

enum pins { SK = 1, DO = 2, DI = 4, TMS = 8, GPIO0 = 16, GPIO1 = 32, GPIO2 = 64, GPIO3 = 128 };

enum gpio_pins {
    GPIOL0 = 0,
    GPIOL1 = 1,
    GPIOL2 = 2,
    GPIOL3 = 3,
    GPIOH0 = 4,
    GPIOH1 = 5,
    GPIOH2 = 6,
    GPIOH3 = 7,
    GPIOH4 = 8,
    GPIOH5 = 9,
    GPIOH6 = 10,
    GPIOH7 = 11
};

enum i2c_ack { ACK = 0, NACK = 1 };

#define DEFAULT_TRIS                                                                               \
    (SK | DO | TMS | GPIO0 | GPIO1 | GPIO2                                                         \
     | GPIO3)                   /* SK/DO/CS and GPIOs are outputs, DI is an input */
#define DEFAULT_PORT (SK | TMS) /* SK and CS are high, all others low */

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,
};

enum low_bits_status { STARTED, STOPPED };

struct vid_pid {
    int   vid;
    int   pid;
    char *description;
};

struct mpsse_context {
    char                *description;
    struct ftdi_context  ftdi;
    enum modes           mode;
    enum low_bits_status status;
    int                  flush_after_read;
    int                  vid;
    int                  pid;
    uint32_t             clock;
    int                  xsize;
    int                  open;
    int                  endianess;
    uint8_t              tris;
    uint8_t              pstart;
    uint8_t              pstop;
    uint8_t              pidle;
    uint8_t              gpioh;
    uint8_t              trish;
    uint8_t              bitbang;
    uint8_t              tx;
    uint8_t              rx;
    uint8_t              txrx;
    uint8_t              tack;
    uint8_t              rack;
};

struct mpsse_context *MPSSE(enum modes mode, int freq, int endianess, enum interface interface);
struct mpsse_context *Open(int         vid,
                           int         pid,
                           enum modes  mode,
                           int         freq,
                           int         endianess,
                           int         interface,
                           const char *description,
                           const char *serial);
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);
void                  Close(struct mpsse_context *mpsse);
const char           *ErrorString(struct mpsse_context *mpsse);
int                   SetMode(struct mpsse_context *mpsse, int endianess);
void                  EnableBitmode(struct mpsse_context *mpsse, int tf);
int                   SetClock(struct mpsse_context *mpsse, uint32_t freq);
uint32_t              GetClock(struct mpsse_context *mpsse);
int                   GetVid(struct mpsse_context *mpsse);
int                   GetPid(struct mpsse_context *mpsse);
const char           *GetDescription(struct mpsse_context *mpsse);
int                   SetLoopback(struct mpsse_context *mpsse, int enable);
void                  SetCSIdle(struct mpsse_context *mpsse, int idle);
int                   Start(struct mpsse_context *mpsse);
int                   Write(struct mpsse_context *mpsse, char *data, int size);
int                   Stop(struct mpsse_context *mpsse);
int                   GetAck(struct mpsse_context *mpsse);
void                  SetAck(struct mpsse_context *mpsse, int ack);
void                  SendAcks(struct mpsse_context *mpsse);
void                  SendNacks(struct mpsse_context *mpsse);
void                  FlushAfterRead(struct mpsse_context *mpsse, int tf);
int                   PinHigh(struct mpsse_context *mpsse, int pin);
int                   PinLow(struct mpsse_context *mpsse, int pin);
int                   SetDirection(struct mpsse_context *mpsse, uint8_t direction);
int                   WriteBits(struct mpsse_context *mpsse, char bits, int size);
char                  ReadBits(struct mpsse_context *mpsse, int size);
int                   WritePins(struct mpsse_context *mpsse, uint8_t data);
int                   ReadPins(struct mpsse_context *mpsse);
int                   PinState(struct mpsse_context *mpsse, int pin, int state);
int                   Tristate(struct mpsse_context *mpsse);

#ifdef SWIGPYTHON
typedef struct swig_string_data {
    int   size;
    char *data;
} swig_string_data;

swig_string_data Read(struct mpsse_context *mpsse, int size);
swig_string_data Transfer(struct mpsse_context *mpsse, char *data, int size);
#else
char *Read(struct mpsse_context *mpsse, int size);
char *Transfer(struct mpsse_context *mpsse, char *data, int size);

int FastWrite(struct mpsse_context *mpsse, char *data, int size);
int FastRead(struct mpsse_context *mpsse, char *data, int size);
int FastTransfer(struct mpsse_context *mpsse, char *wdata, char *rdata, int size);
#endif

#endif
