summaryrefslogtreecommitdiff
path: root/loragw_hal/src/loragw_spi.ftdi.c
diff options
context:
space:
mode:
Diffstat (limited to 'loragw_hal/src/loragw_spi.ftdi.c')
-rw-r--r--loragw_hal/src/loragw_spi.ftdi.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/loragw_hal/src/loragw_spi.ftdi.c b/loragw_hal/src/loragw_spi.ftdi.c
new file mode 100644
index 0000000..7b71901
--- /dev/null
+++ b/loragw_hal/src/loragw_spi.ftdi.c
@@ -0,0 +1,285 @@
+/*
+ / _____) _ | |
+( (____ _____ ____ _| |_ _____ ____| |__
+ \____ \| ___ | (_ _) ___ |/ ___) _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+ ©2013 Semtech-Cycleo
+
+Description:
+ Host specific functions to address the LoRa™ gateway registers through a
+ SPI interface.
+ Single-byte read/write and burst read/write.
+ Does not handle pagination.
+ Could be used with multiple SPI ports in parallel (explicit file descriptor)
+*/
+
+
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <stdint.h> /* C99 types */
+#include <stdio.h> /* printf fprintf */
+#include <stdlib.h> /* malloc free */
+#include <string.h> /* memcpy */
+
+#include <mpsse.h>
+
+#include "loragw_spi.h"
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MACROS ------------------------------------------------------- */
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#if DEBUG_SPI == 1
+ #define DEBUG_MSG(str) fprintf(stderr, str)
+ #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
+ #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;}
+#else
+ #define DEBUG_MSG(str)
+ #define DEBUG_PRINTF(fmt, args...)
+ #define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;}
+#endif
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
+
+#define READ_ACCESS 0x00
+#define WRITE_ACCESS 0x80
+
+/* parameters for a FT2232H */
+#define VID 0x0403
+#define PID 0x6010
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
+
+/* SPI initialization and configuration */
+int lgw_spi_open(void **spi_target_ptr) {
+ struct mpsse_context *mpsse = NULL;
+
+ /* check input variables */
+ CHECK_NULL(spi_target_ptr); /* cannot be null, must point on a void pointer (*spi_target_ptr can be null) */
+
+ /* try to open the first available FTDI device matching VID/PID parameters */
+ mpsse = OpenIndex(VID,PID,SPI0, SIX_MHZ, MSB, IFACE_A, NULL, NULL, 0);
+ if (mpsse == NULL) {
+ DEBUG_MSG("ERROR: MPSSE OPEN FUNCTION RETURNED NULL\n");
+ return LGW_SPI_ERROR;
+ }
+ if (mpsse->open != 1) {
+ DEBUG_MSG("ERROR: MPSSE OPEN FUNCTION FAILED\n");
+ return LGW_SPI_ERROR;
+ }
+
+ DEBUG_PRINTF("SPI port opened and configured ok\ndesc: %s\nPID: 0x%04X\nVID: 0x%04X\nclock: %d\nLibmpsse version: 0x%02X\n", GetDescription(mpsse), GetPid(mpsse), GetVid(mpsse), GetClock(mpsse), Version());
+ *spi_target_ptr = (void *)mpsse;
+ return LGW_SPI_SUCCESS;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* SPI release */
+int lgw_spi_close(void *spi_target) {
+ struct mpsse_context *mpsse = spi_target;
+
+ /* check input variables */
+ CHECK_NULL(spi_target);
+
+ Close(mpsse);
+
+ /* close return no status, assume success (0_o) */
+ return LGW_SPI_SUCCESS;
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* Simple write */
+/* transaction time: .6 to 1 ms typically */
+int lgw_spi_w(void *spi_target, uint8_t address, uint8_t data) {
+ struct mpsse_context *mpsse = spi_target;
+ uint8_t out_buf[2];
+ int a, b, c;
+
+ /* check input variables */
+ CHECK_NULL(spi_target);
+ if ((address & 0x80) != 0) {
+ DEBUG_MSG("WARNING: SPI address > 127\n");
+ }
+
+ /* prepare frame to be sent */
+ out_buf[0] = WRITE_ACCESS | (address & 0x7F);
+ out_buf[1] = data;
+
+ /* MPSSE transaction */
+ a = Start(mpsse);
+ b = FastWrite(mpsse, (char *)out_buf, 2);
+ c = Stop(mpsse);
+
+ /* determine return code */
+ if ((a != MPSSE_OK) || (b != MPSSE_OK) || (c != MPSSE_OK)) {
+ DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");
+ return LGW_SPI_ERROR;
+ } else {
+ DEBUG_MSG("Note: SPI write success\n");
+ return LGW_SPI_SUCCESS;
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* Simple read (using Transfer function) */
+/* transaction time: 1.1 to 2 ms typically */
+int lgw_spi_r(void *spi_target, uint8_t address, uint8_t *data) {
+ struct mpsse_context *mpsse = spi_target;
+ uint8_t out_buf[2];
+ uint8_t *in_buf = NULL;
+ int a, b;
+
+ /* check input variables */
+ CHECK_NULL(spi_target);
+ if ((address & 0x80) != 0) {
+ DEBUG_MSG("WARNING: SPI address > 127\n");
+ }
+ CHECK_NULL(data);
+
+ /* prepare frame to be sent */
+ out_buf[0] = READ_ACCESS | (address & 0x7F);
+ out_buf[1] = 0x00;
+
+ /* MPSSE transaction */
+ a = Start(mpsse);
+ in_buf = (uint8_t *)Transfer(mpsse, (char *)out_buf, 2);
+ b = Stop(mpsse);
+
+ /* determine return code */
+ if ((in_buf == NULL) || (a != MPSSE_OK) || (b != MPSSE_OK)) {
+ DEBUG_MSG("ERROR: SPI READ FAILURE\n");
+ if (in_buf != NULL) {
+ free(in_buf);
+ }
+ return LGW_SPI_ERROR;
+ } else {
+ DEBUG_MSG("Note: SPI read success\n");
+ *data = in_buf[1];
+ free(in_buf);
+ return LGW_SPI_SUCCESS;
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* Burst (multiple-byte) write */
+/* transaction time: 3.7ms for 2500 data bytes @6MHz, 1kB chunks */
+/* transaction time: 0.5ms for 16 data bytes @6MHz, 1kB chunks */
+int lgw_spi_wb(void *spi_target, uint8_t address, uint8_t *data, uint16_t size) {
+ struct mpsse_context *mpsse = spi_target;
+ uint8_t command;
+ uint8_t *out_buf = NULL;
+ int size_to_do, buf_size, chunk_size, offset;
+ int a, b, c;
+ int i;
+
+ /* check input parameters */
+ CHECK_NULL(spi_target);
+ if ((address & 0x80) != 0) {
+ DEBUG_MSG("WARNING: SPI address > 127\n");
+ }
+ CHECK_NULL(data);
+ if (size == 0) {
+ DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");
+ return LGW_SPI_ERROR;
+ }
+
+ /* prepare command byte */
+ command = WRITE_ACCESS | (address & 0x7F);
+ size_to_do = size + 1; /* add a byte for the address */
+
+ /* allocate data buffer */
+ buf_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;
+ out_buf = malloc(buf_size);
+ if (out_buf == NULL) {
+ DEBUG_MSG("ERROR: MALLOC FAIL\n");
+ return LGW_SPI_ERROR;
+ }
+
+ /* start MPSSE transaction */
+ a = Start(mpsse);
+ for (i=0; size_to_do > 0; ++i) {
+ chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;
+ if (i == 0) {
+ /* first chunk, need to append the address */
+ out_buf[0] = command;
+ memcpy(out_buf+1, data, chunk_size-1);
+ } else {
+ /* following chunks, just copy the data */
+ offset = (i * LGW_BURST_CHUNK) - 1;
+ memcpy(out_buf, data + offset, chunk_size);
+ }
+ b = FastWrite(mpsse, (char *)out_buf, chunk_size);
+ size_to_do -= chunk_size; /* subtract the quantity of data already transferred */
+ }
+ c = Stop(mpsse);
+
+ /* deallocate data buffer */
+ free(out_buf);
+
+ /* determine return code (only the last FastWrite is checked) */
+ if ((a != MPSSE_OK) || (b != MPSSE_OK) || (c != MPSSE_OK)) {
+ DEBUG_MSG("ERROR: SPI BURST WRITE FAILURE\n");
+ return LGW_SPI_ERROR;
+ } else {
+ DEBUG_MSG("Note: SPI burst write success\n");
+ return LGW_SPI_SUCCESS;
+ }
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* Burst (multiple-byte) read (using FastWrite & FastRead functions) */
+/* transaction time: 7-12ms for 2500 data bytes @6MHz, 1kB chunks */
+/* transaction time: 2ms for 16 data bytes @6MHz, 1kB chunks */
+int lgw_spi_rb(void *spi_target, uint8_t address, uint8_t *data, uint16_t size) {
+ struct mpsse_context *mpsse = spi_target;
+ uint8_t command;
+ int size_to_do, chunk_size, offset;
+ int a, b, c, d;
+ int i;
+
+ /* check input parameters */
+ CHECK_NULL(spi_target);
+ if ((address & 0x80) != 0) {
+ DEBUG_MSG("WARNING: SPI address > 127\n");
+ }
+ CHECK_NULL(data);
+ if (size == 0) {
+ DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");
+ return LGW_SPI_ERROR;
+ }
+
+ /* prepare command byte */
+ command = READ_ACCESS | (address & 0x7F);
+ size_to_do = size;
+
+ /* start MPSSE transaction */
+ a = Start(mpsse);
+ b = FastWrite(mpsse, (char *)&command, 1);
+ for (i=0; size_to_do > 0; ++i) {
+ chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;
+ offset = i * LGW_BURST_CHUNK;
+ c = FastRead(mpsse, (char *)(data + offset), chunk_size);
+ size_to_do -= chunk_size; /* subtract the quantity of data already transferred */
+ }
+ d = Stop(mpsse);
+
+ /* determine return code (only the last FastRead is checked) */
+ if ((a != MPSSE_OK) || (b != MPSSE_OK) || (c != MPSSE_OK) || (d != MPSSE_OK)) {
+ DEBUG_MSG("ERROR: SPI BURST READ FAILURE\n");
+ return LGW_SPI_ERROR;
+ } else {
+ DEBUG_MSG("Note: SPI burst read success\n");
+ return LGW_SPI_SUCCESS;
+ }
+}
+
+/* --- EOF ------------------------------------------------------------------ */