summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Klug <john.klug@multitech.com>2017-08-18 15:14:17 -0500
committerJohn Klug <john.klug@multitech.com>2017-08-18 15:14:17 -0500
commit3ff432faedee2ec0b93d163e4808f391d65f1ba2 (patch)
tree59df462ea8586a664a59d4cf62f1a58af6507f7e
parentc2da95882ea7a4218a4432ee5da8165aa61addfc (diff)
downloadmts-id-eeprom-3ff432faedee2ec0b93d163e4808f391d65f1ba2.tar.gz
mts-id-eeprom-3ff432faedee2ec0b93d163e4808f391d65f1ba2.tar.bz2
mts-id-eeprom-3ff432faedee2ec0b93d163e4808f391d65f1ba2.zip
mts-fpga-loader
-rw-r--r--src/Makefile.am7
-rw-r--r--src/README.md51
-rw-r--r--src/mts_error_codes.h24
-rw-r--r--src/mts_fpga_hash.c127
-rw-r--r--src/mts_fpga_hash.h14
-rw-r--r--src/mts_fpga_loader.c85
-rw-r--r--src/mts_fpga_reg.c406
-rw-r--r--src/mts_fpga_reg.h75
-rw-r--r--src/mts_fpga_spi.c513
-rw-r--r--src/mts_fpga_spi.h68
10 files changed, 1368 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 12f084c..6884e1a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,12 +3,15 @@ AUTOMAKE_OPTIONS = gnu
AM_CFLAGS = -Wall
bin_PROGRAMS = mts-id-eeprom
-sbin_PROGRAMS = mts-hashpwd
+sbin_PROGRAMS = mts-hashpwd mts-fpga-loader
mts_hashpwd_SOURCES = hashpwd.cpp
mts_id_eeprom_SOURCES = eeprom_main.c
-noinst_HEADERS = eeprom.h
+noinst_HEADERS = eeprom.h log.h mts_error_codes.h mts_fpga_hash.h mts_fpga_reg.h mts_fpga_spi.h
mts_hashpwd_LDADD = -lcrypto
+mts_fpga_loader_SOURCES = mts_fpga_hash.c mts_fpga_reg.c mts_fpga_spi.c mts_fpga_loader.c
+mts_fpga_loader_LDADD = -lrt -lm -lssl -lcrypto
+
sbin_SCRIPTS = ubpasswd.sh
EXTRA_DIST =
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..0d0eef5
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,51 @@
+# MTAC Upgrade Utility
+
+This Utility allows upgrading the FPGA firmware on the MTAC-LoRa 1.5 rev c cards.
+It currently supports the following versions:
+- v28 (TX Filter, Spectral Scan)
+- v31 (TX Filter, Spectral Scan)
+
+## Getting Started
+
+These instructions will get you to upgrade the FPGA on a MTAC-LoRa 1.5 rev c card.
+
+## Prerequisites
+
+MultiConnect Conduit and MTAC-LoRa 1.5 rev c card or MultiConnect Access Point.
+
+## Installing
+
+```
+scp <files> <device ip address>
+tar -xvf <files>
+ssh <device ip address>
+chmod +x mtac_upgrade
+```
+
+## Usage
+Please stop any processes that try to access the MTAC-LoRa card. For example, the LoRa Network Server and the LoRa Packet Forwarder or other third-party packet forwarders (e.g. Loriot, TTN, Actility, Senet, Orbiwise, etc.). The utility will check for open files on spidev and will not run if it finds any to prevent multiple access.
+
+### Example
+```
+./mtac_upgrade -h See all available options and examples
+./mtac_upgrade -c Check existing FPGA version
+./mtac_upgrade -i <upgrade file> Upgrade the FPGA on the default port using the file specified
+./mtac_upgrade -p 2 -i <upgrade file> Upgrade the FPGA on ap2 using the file specified
+./mtac_upgrade Upgrade the FPGA on the default port and upgrade file
+```
+### Options
+```
+ -c Check Existing MTAC Verson
+ -i Specify input file. Default is mtcdt-fpga-v31.hex for a MultiConnect Conduit & mtcap-fpga-v31.hex for MultiConnect Access Point
+ -p Specify port 1 or 2. Only required for a MultiConnect Conduit when dual cards are installed
+ -s Print Supported FPGA versions
+```
+
+## Authors
+
+* **Harsh Sharma** - Initial work
+
+## Acknowledgments
+
+* **Tim Barr** - Hardware design reference
+
diff --git a/src/mts_error_codes.h b/src/mts_error_codes.h
new file mode 100644
index 0000000..52346c7
--- /dev/null
+++ b/src/mts_error_codes.h
@@ -0,0 +1,24 @@
+#define MTAC_SUCCESS 0 /* Operation success */
+
+/* Hardware Errors */
+#define MTAC_INVALID_FPGA_VERSION 1 /* FPGA version check failed */
+#define MTAC_FILE_NOT_FOUND 2 /* Linked upgrade file not found */
+#define MTAC_INVALID_HARDWARE 3 /* MTAC Hardware does not support FPGA Upgrade */
+#define MTAC_HW_NOT_FOUND 4 /* MTAC Not found */
+#define MTAC_LOCK_FAILURE 5 /* Creset pin write failure */
+#define MTAC_OPEN_FILES 6
+
+/* SPI Errors */
+#define MTAC_SPI_OPEN_FAILURE 7 /* SPI open was not successful */
+#define MTAC_SPI_TRANSFER_FAILURE 8 /* IOCTL transfer failed */
+#define MTAC_SPI_CLOSE_FAILURE 9 /* SPI close was not successful */
+#define MTAC_SPI_ERROR 10 /* Generic SPI error */
+
+/* Register Errors*/
+#define MTAC_FPGA_REG_ERROR 11 /* Error reading FPGA Version */
+#define MTAC_SX1301_VERSION_FAILURE 12 /* Error reading SX1301 Version */
+#define MTAC_HASH_VERIFICATION_FAILURE 13 /* Input file's calculated hash did not match */
+
+/* Flashing Failures*/
+#define MTAC_CHIP_ERASE_FAILURE 14 /* Chip erase failure */
+#define MTAC_PROGRAM_FAILURE 15 /* Flashing mtac with firmware failed */
diff --git a/src/mts_fpga_hash.c b/src/mts_fpga_hash.c
new file mode 100644
index 0000000..7bd4da4
--- /dev/null
+++ b/src/mts_fpga_hash.c
@@ -0,0 +1,127 @@
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+#include <errno.h>
+#include <fcntl.h> /* open */
+#include <openssl/sha.h> /* generate hash */
+#include <stdio.h> /* printf fprintf */
+#include <stdlib.h> /* malloc free */
+#include <stdint.h> /* C99 types */
+#include <string.h> /* memset */
+#include "mts_error_codes.h" /* error codes*/
+#include "mts_fpga_reg.h" /* register functions*/
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MACROS ------------------------------------------------------- */
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#if DEBUG_HASH == 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 MTAC_REG_ERROR; \
+ }
+#else
+#define DEBUG_MSG(str)
+#define DEBUG_PRINTF(fmt, args...)
+#define CHECK_NULL(a) \
+ if (a == NULL) { \
+ return MTAC_REG_ERROR; \
+ }
+#endif
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
+
+static const char *valid_mtcdt_hashes[] = {
+ "d9f811fcab57947db3c2323242885a32a7f095a069d3386a148466e7f3da5353", /* v28*/
+ "903c1199df46d38683b1aa9fc88310abe2f317c01c3aefa77987990874aba420", /* v31*/
+ "7c190506b969aea6198daffb6c9b47685f3a4dc3ce18565c66542bac27d6f24e"};/* v33*/
+static const char *valid_mtcap_hashes[] = {
+ "07317fe9ca59393c074215c9d923d8d01025654883291a5e89b27d21668e2263", /* v28*/
+ "f208ef5cae03e703951bb8799172a5eaadb74ddb90bf3e65c32030c008a88e75", /* v31*/
+ "aaecd468b187703dbbf76022b00268dba2a5f25300da6486d420f476c836385c"};/* v33*/
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE FUNCTIONS DEFINITION ------------------------------------------ */
+
+/* hash outputBuffer */
+static void sha256_hash_string(char hash[SHA256_DIGEST_LENGTH],
+ char outputBuffer[65]) {
+ int i = 0;
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ sprintf(outputBuffer + (i * 2), "%02x", (unsigned char)hash[i]);
+ }
+ outputBuffer[64] = 0;
+ DEBUG_PRINTF("OutputBuffer hash: %s\n", outputBuffer);
+}
+
+/* Initialize, update and finalize sha256 */
+static void sha256(char *string, char outputBuffer[65]) {
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ int len;
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ len = strlen(string);
+ SHA256_Update(&sha256, string, len);
+ SHA256_Final(hash, &sha256);
+ int i = 0;
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ sprintf(outputBuffer + (i * 2), "%02x", (unsigned char)hash[i]);
+ }
+ outputBuffer[64] = 0;
+ DEBUG_PRINTF("OutputBuffer finalized: %s\n", outputBuffer);
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
+
+/* Open input file and verify sha256 with verified list */
+int sha256_file(char* hw, char *path) {
+ DEBUG_PRINTF("Checking hash on input file: %s\n", path);
+ FILE *file = fopen(path, "rb");
+ if (!file) {
+ printf("File %s not found\n", path);
+ return MTAC_FILE_NOT_FOUND;
+ }
+ else {
+ printf("Checking file %s\n", path);
+ }
+ char file_hash[65];
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ const int bufSize = 32768;
+ unsigned char *buffer = malloc(bufSize);
+ int bytesRead = 0;
+ if (!buffer)
+ return ENOMEM;
+ while ((bytesRead = fread(buffer, 1, bufSize, file))) {
+ SHA256_Update(&sha256, buffer, bytesRead);
+ }
+ SHA256_Final(hash, &sha256);
+ sha256_hash_string(hash, file_hash);
+ fclose(file);
+ free(buffer);
+ DEBUG_PRINTF("Calculated input file hash: %s\n", file_hash);
+ int i;
+ if (strstr(hw, MTCAP)) {
+ for(i = 0; i < sizeof(valid_mtcap_hashes)/sizeof(valid_mtcap_hashes[0]); ++i) {
+ if(!strcmp(valid_mtcap_hashes[i], file_hash)) {
+ printf("File verified\n");
+ return MTAC_SUCCESS;
+ }
+ }
+ }
+ else if (strstr(hw, MTCDT)) {
+ for(i = 0; i < sizeof(valid_mtcdt_hashes)/sizeof(valid_mtcdt_hashes[0]); ++i) {
+ if(!strcmp(valid_mtcdt_hashes[i], file_hash)) {
+ printf("File verified\n");
+ return MTAC_SUCCESS;
+ }
+ }
+ }
+ printf("Invalid input file.\n");
+ return MTAC_HASH_VERIFICATION_FAILURE;
+} \ No newline at end of file
diff --git a/src/mts_fpga_hash.h b/src/mts_fpga_hash.h
new file mode 100644
index 0000000..a45df87
--- /dev/null
+++ b/src/mts_fpga_hash.h
@@ -0,0 +1,14 @@
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <openssl/sha.h> /* generate hash */
+#include <stdint.h> /* C99 types */
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
+static void sha256_hash_string(char hash[SHA256_DIGEST_LENGTH], char outputBuffer[65]);
+
+static void sha256(char *string, char outputBuffer[65]);
+
+int sha256_file(char* hw, char *path);
+/* --- EOF ------------------------------------------------------------------ */
diff --git a/src/mts_fpga_loader.c b/src/mts_fpga_loader.c
new file mode 100644
index 0000000..3648eeb
--- /dev/null
+++ b/src/mts_fpga_loader.c
@@ -0,0 +1,85 @@
+#include <stdio.h> /* printf fprintf */
+#include <unistd.h> /* getopt */
+
+#include "mts_error_codes.h" /* list of utility error codes */
+#include "mts_fpga_reg.h" /* core utility functions a*/
+
+/* -------------------------------------------------------------------------- */
+/* --- MACROS & CONSTANTS --------------------------------------------------- */
+#define DEFAULT_PATH 0 /* default path, points to spidev0.0 */
+
+void usage(int argc, char *argv[]) {
+ printf("Usage: %s -option <argument>\n", argv[0]);
+ printf("Example\n");
+ printf(" %s -h See all available options and examples\n", argv[0]);
+ printf(" %s -c Check existing FPGA version\n", argv[0]);
+ printf(" %s -i <upgrade file> Upgrade the FPGA on the default port using the file specified\n", argv[0]);
+ printf(" %s -p 2 -i <upgrade file> Upgrade the FPGA on ap2 using the file specified\n", argv[0]);
+ printf(" %s Upgrade the FPGA on the default port and upgrade file\n", argv[0]);
+ printf("Options\n");
+ printf(" -c Check FPGA Version on MTAC card\n");
+ printf(" -i Specify input file. Default is mtcdt-fpga-v31.hex for a MultiConnect Conduit & mtcap-fpga-v31.hex for MultiConnect Access Point\n");
+ printf(" -p Specify port 1 or 2. Only required for a MultiConnect Conduit when dual cards are installed\n");
+ printf(" -s Print FPGA versions supported by the utility\n");
+}
+
+
+int main(int argc, char **argv) {
+ int index;
+ int c;
+ int ret;
+ char *upgrade_file;
+ int path = DEFAULT_PATH;
+ opterr = 0;
+ while ((c = getopt(argc, argv, "shp:ci:")) != -1)
+ switch (c) {
+ case 's':
+ print_supported_versions();
+ return 0;
+ case 'h':
+ usage(argc, argv);
+ return 0;
+ case 'p':
+ path = atoi(optarg);
+ if (path == 1) {
+ printf("Using ap1 - /dev/spidev32766.2\n");
+ }
+ else if (path == 2) {
+ printf("Using ap2 - /dev/spidev32765.2\n");
+ }
+ else {
+ fprintf(stderr, "Path %d must be set to 1(ap1) or 2(ap2)\n", path);
+ return 1;
+ }
+ break;
+ case 'i':
+ upgrade_file = optarg;
+ break;
+ case 'c':
+ ret = mtac_get_version(path);
+ if (ret != MTAC_SUCCESS) {
+ fprintf(stderr, "Version Check failed. Error Code: %d\n", ret);
+ return 1;
+ }
+ return 0;
+ case '?':
+ if (optopt == 'i')
+ fprintf(stderr, "Option -%c requires a file argument.\n", optopt);
+ else if (isprint(optopt))
+ fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+ else
+ fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+ return 1;
+ default:
+ usage(argc, argv);
+ }
+ ret = mtac_upgrade(upgrade_file, path);
+ if (ret != MTAC_SUCCESS) {
+ fprintf(stderr, "Upgrade Failed, Error Code: %d\n", ret);
+ return 1;
+ } else {
+ printf("FPGA Upgrade Sucessful\n");
+ return 0;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/src/mts_fpga_reg.c b/src/mts_fpga_reg.c
new file mode 100644
index 0000000..6955219
--- /dev/null
+++ b/src/mts_fpga_reg.c
@@ -0,0 +1,406 @@
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <errno.h>
+#include <fcntl.h> /* open */
+#include <stdlib.h> /* malloc free */
+#include <string.h> /* memset */
+#include "mts_fpga_reg.h" /* register functions*/
+#include "mts_error_codes.h" /* error codes*/
+#include "mts_fpga_hash.h" /* compute fiel hash*/
+#include "mts_fpga_spi.h" /* spi interface commands*/
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE MACROS ------------------------------------------------------- */
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#if DEBUG_REG == 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 MTAC_REG_ERROR; \
+ }
+#else
+#define DEBUG_MSG(str)
+#define DEBUG_PRINTF(fmt, args...)
+#define CHECK_NULL(a) \
+ if (a == NULL) { \
+ return MTAC_REG_ERROR; \
+ }
+#endif
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
+
+const char *valid_hw[] = {MTCDT, MTCAP};
+static const uint8_t fpga_version[] = {28, 31, 33};
+static const struct mtac_reg_s loregs[3] = {
+ {-1, 0, 0, 0, 2, 0, 0}, /* PAGE_REG */
+ {-1, 0, 7, 0, 1, 0, 0}, /* SOFT_RESET */
+ {-1, 1, 0, 0, 8, 1, 103} /* VERSION */
+};
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE VARIABLES ---------------------------------------------------- */
+
+void *mtac_spi_target = NULL; /*! generic pointer to the SPI device */
+uint8_t mtac_spi_mux_mode = 0; /*! current SPI mux mode used */
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
+
+void print_supported_versions() {
+ int i;
+ printf("Supported FPGA Versions: \n");
+ for (i = 0; i < (int)(sizeof fpga_version); i++) {
+ printf("%d\n", fpga_version[i]);
+ }
+}
+
+/* check the version of the fpga is within the supported list */
+bool check_fpga_version(uint8_t version) {
+ int i;
+ for (i = 0; i < (int)(sizeof fpga_version); i++) {
+ if (fpga_version[i] == version) {
+ printf("Valid FPGA version: %u\n", version);
+ return true;
+ }
+ }
+ printf("Invalid FPGA version: %u\n", version);
+ return false;
+}
+
+/* Make sure no device is using the spidev bus to prevent contention*/
+int bus_contention() {
+ char number[1024];
+ FILE *f = popen("lsof", "r");
+ while (fgets(number, 1024, f) != NULL) {
+ if (strstr(number, "spidev") != NULL) {
+ printf("The accesory card is being used by another process: \n %sPlease "
+ "close all LoRaWAN processes and try again.\n",
+ number);
+ pclose(f);
+ return MTAC_OPEN_FILES;
+ }
+ }
+ pclose(f);
+ return MTAC_SUCCESS;
+}
+
+/* write to creset pin */
+int mtac_creset_write(mts_hw *mts, char num) {
+ int fd = open(mts->creset, O_WRONLY);
+ if (fd < 0) {
+ printf("Unable to lock file, are you root?\n");
+ return MTAC_LOCK_FAILURE;
+ }
+ write(fd, &num, 1);
+ close(fd);
+ return MTAC_SUCCESS;
+}
+
+int hw_check(mts_hw *mts) {
+ FILE *fp;
+ fp = fopen(HW_FILE, "r");
+
+ if (fp == NULL) {
+ printf("Unable to lock file, are you root?\n");
+ return MTAC_LOCK_FAILURE;
+ }
+ fscanf(fp, "%[^\n]", mts->dev_hw);
+ fclose(fp);
+ if (strstr(mts->dev_hw, "MTCAP")) {
+ if (mts->path != 0) {
+ printf("Path argument is not supported on this device. Exiting.\n");
+ return MTAC_INVALID_HARDWARE;
+ } else {
+ strcpy(mts->spi_path, SPI_DEV_PATH_MTCAP);
+ strcpy(mts->mtac_hw, MTAC_HW_VERSION);
+ strcpy(mts->creset, CRESET);
+ return MTAC_SUCCESS;
+ }
+ } else if (strstr(mts->dev_hw, "MTCDT")) {
+ if (mts->path == 0) {
+ strcpy(mts->spi_path, SPI_DEV_PATH_MTCDT);
+ strcpy(mts->mtac_hw, MTAC_HW_VERSION);
+ strcpy(mts->creset, CRESET);
+ return MTAC_SUCCESS;
+ } else if (mts->path == 1) {
+ strcpy(mts->mtac_hw, MTAC_HW_VERSION_AP1);
+ strcpy(mts->spi_path, SPI_DEV_PATH_AP1);
+ strcpy(mts->creset, CRESET_AP1);
+ return MTAC_SUCCESS;
+ } else if (mts->path == 2) {
+ strcpy(mts->mtac_hw, MTAC_HW_VERSION_AP2);
+ strcpy(mts->spi_path, SPI_DEV_PATH_AP2);
+ strcpy(mts->creset, CRESET_AP2);
+ return MTAC_SUCCESS;
+ }
+ } else
+ printf("This software is licensed only for use on MultiTech products.\n");
+ return MTAC_INVALID_HARDWARE;
+}
+
+/* check mtac hardware compatibility */
+int mtac_check(mts_hw *mts) {
+ int i;
+ char buff[255];
+ FILE *fptr;
+ /* verify that the device to be flashed exists */
+ if ((fptr = fopen(mts->mtac_hw, "r")) == NULL) {
+ printf("Could not connect to hardware. Verify accessory port argument?\n");
+ return MTAC_HW_NOT_FOUND;
+ }
+ fscanf(fptr, "%s", &buff);
+ for (i = 0; i < (int)(sizeof valid_hw); i++) {
+ if (valid_hw[i] == buff) {
+ printf("Found Valid Hardware: %s\n", buff);
+ strcpy(mts->mtac_hw, buff);
+ fclose(fptr);
+ return MTAC_SUCCESS;
+ }
+ }
+ printf("\nInvalid Hardware: %s. Cannot Upgrade FPGA.\n", buff);
+ fclose(fptr);
+ return MTAC_INVALID_HARDWARE;
+}
+
+/* connect to the oncentrator to get the fpga version */
+int mtac_get_version(int path) {
+ uint8_t u = 0;
+ int ret;
+ mts_hw mts;
+ mts.path = path;
+ /* check MTAC Hardware Compatibility */
+ printf("Checking hardware compatibility\n");
+ ret = hw_check(&mts);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+
+ ret = mtac_check(&mts);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+
+ if (strstr(mts.mtac_hw, MTCAP)) {
+ strcpy(mts.spi_path, "/dev/spidev0.0");
+ }
+ /* open the SPI link */
+ ret = mtac_creset_write(&mts, '1');
+ sleep(2);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ ret = mtac_spi_open(mts.spi_path, &mtac_spi_target);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ /* detect if the gateway has an FPGA with SPI mux header support */
+ ret = mtac_spi_r(mtac_spi_target, MTAC_SPI_MUX_MODE1, MTAC_FPGA,
+ loregs[MTAC_VERSION].addr, &u);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ if (check_fpga_version(u) != true) {
+ printf("INFO: no FPGA detected or version not supported (v%u)\n", u);
+ return MTAC_INVALID_FPGA_VERSION;
+ } else {
+ mtac_spi_mux_mode = MTAC_SPI_MUX_MODE1;
+ /* FPGA Soft Reset */
+ mtac_spi_w(mtac_spi_target, mtac_spi_mux_mode, MTAC_FPGA, 0, 1);
+ mtac_spi_w(mtac_spi_target, mtac_spi_mux_mode, MTAC_FPGA, 0, 0);
+ }
+
+ /* check SX1301 version */
+ ret = mtac_spi_r(mtac_spi_target, mtac_spi_mux_mode, MTAC_SX1301,
+ loregs[MTAC_VERSION].addr, &u);
+ if (ret != MTAC_SUCCESS) {
+ return MTAC_SX1301_VERSION_FAILURE;
+ }
+
+ /* write 0 to the page/reset register */
+ ret = mtac_spi_w(mtac_spi_target, mtac_spi_mux_mode, MTAC_SX1301,
+ loregs[MTAC_PAGE_REG].addr, 0);
+ if (ret != MTAC_SUCCESS) {
+ return MTAC_FPGA_REG_ERROR;
+ }
+ mtac_disconnect();
+ return MTAC_SUCCESS;
+}
+
+/* setup and upgrade the mtac card with the file specified */
+int mtac_upgrade(char *input_file, int path) {
+ int ret;
+ mts_hw mts;
+ mts.path = path;
+ /* check that no other device is using the bus */
+ ret = bus_contention();
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ /* check MTAC Hardware Compatibility */
+ printf("Checking hardware compatibility\n");
+ ret = hw_check(&mts);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+
+ ret = mtac_check(&mts);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ if (!input_file) {
+ if (strstr(mts.dev_hw, "MTCDT")) {
+ input_file = strdup(MTCDT_DEFAULT_FILE);
+ } else if (strstr(mts.mtac_hw, MTCAP)) {
+ input_file = strdup(MTCAP_DEFAULT_FILE);
+ }
+ }
+ /* check input file checksum */
+ ret = sha256_file(mts.mtac_hw, input_file);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+
+ /* check SPI link status */
+ if (mtac_spi_target != NULL) {
+ printf("WARNING: concentrator was connected\n");
+ mtac_spi_close(mtac_spi_target);
+ }
+
+ /* pull creset down to access spi flash */
+ ret = mtac_creset_write(&mts, '0');
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ sleep(1);
+
+ /* erase chip before flashing new firmware */
+ ret = mtac_erase(&mts);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+
+ /* program user specified firmware */
+ printf("Programming flash\n");
+ ret = mtac_program(&mts, input_file);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ } else {
+ printf("Write Complete. Resetting FPGA\n");
+ }
+ /* pull creset up to access FPGA */
+ ret = mtac_creset_write(&mts, '1');
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ sleep(5);
+ printf("Reading New FPGA configuration\n");
+ ret = mtac_get_version(path);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ return MTAC_SUCCESS;
+}
+
+/* erase entire flash */
+int mtac_erase(mts_hw *mts) {
+ int ret;
+ printf("Erasing flash\n");
+ /* pull device out of powerdown state */
+ ret = mtac_release(mts->spi_path, mtac_spi_target);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+
+ /* enable writing to flash */
+ ret = write_enable(mts->spi_path, mtac_spi_target);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+
+ /* send chip erase command to flash */
+ ret = chip_erase(mts->spi_path, mtac_spi_target);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ /* pull device out of powerdown state */
+ mtac_release(mts->spi_path, mtac_spi_target);
+ if (ret != MTAC_SUCCESS) {
+ return ret;
+ }
+ return MTAC_SUCCESS;
+}
+
+/* write to mtac card with input file */
+int mtac_program(mts_hw *mts, char input_file[]) {
+ FILE *f;
+ struct stat st;
+ size_t file_size;
+ int i = 0;
+ int ret = MTAC_SUCCESS;
+ int index, result;
+ uint32_t offset, page_address, no_pages, page_data[256];
+ uint8_t adr_lower, adr_higher;
+
+ /* get data array from file to be writen */
+ f = fopen(input_file, "r");
+ stat(input_file, &st);
+ file_size = st.st_size;
+ uint8_t *p_array;
+ p_array = (uint8_t *)malloc(sizeof(uint8_t) * file_size);
+ unsigned int data[file_size];
+ while ((result = fscanf(f, "%x ", data + i)) == 1) {
+ i++;
+ }
+ fclose(f);
+ no_pages = ceil(i / 256.0);
+
+ /* program one page at a time */
+ for (page_address = 0x00; page_address < no_pages; page_address++) {
+ /* mask page address */
+ adr_higher = (page_address >> 8) & 0xff;
+ adr_lower = page_address & 0xff;
+ /* calculate initial data offset */
+ offset = page_address * 256;
+ /* enable writing to flash */
+ ret = write_enable(mts->spi_path, mtac_spi_target);
+ if (ret != MTAC_SUCCESS) {
+ free(p_array);
+ break;
+ }
+ /* assign data for page to be written */
+ for (index = 0; index < 256; index++) {
+ page_data[index] = index + offset > i ? 0xff : data[index + offset];
+ }
+ /* program single page*/
+ ret = page_program(mts->spi_path, mtac_spi_target, adr_lower, adr_higher, page_data);
+ if (ret != MTAC_SUCCESS) {
+ free(p_array);
+ break;
+ }
+ /* release device from page program powerdown */
+ ret = mtac_release(mts->spi_path, mtac_spi_target);
+ if (ret != MTAC_SUCCESS) {
+ free(p_array);
+ break;
+ }
+ }
+ free(p_array);
+ return ret;
+}
+
+/* Concentrator disconnect */
+int mtac_disconnect(void) {
+ if (mtac_spi_target != NULL) {
+ mtac_spi_close(mtac_spi_target);
+ mtac_spi_target = NULL;
+ DEBUG_MSG("Note: success disconnecting the concentrator\n");
+ return MTAC_SUCCESS;
+ } else {
+ DEBUG_MSG("WARNING: concentrator was already disconnected\n");
+ return MTAC_SPI_CLOSE_FAILURE;
+ }
+} \ No newline at end of file
diff --git a/src/mts_fpga_reg.h b/src/mts_fpga_reg.h
new file mode 100644
index 0000000..d67a60c
--- /dev/null
+++ b/src/mts_fpga_reg.h
@@ -0,0 +1,75 @@
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+#include <stdbool.h> /* bool type */
+#include <stdint.h> /* C99 types */
+#include <stdio.h>
+
+/* -------------------------------------------------------------------------- */
+/* --- INTERNAL SHARED TYPES ------------------------------------------------ */
+
+struct mtac_reg_s {
+ int8_t page; /*!< page containing the register (-1 for all pages) */
+ uint8_t addr; /*!< base address of the register (7 bit) */
+ uint8_t offs; /*!< position of the register LSB (between 0 to 7) */
+ bool sign; /*!< 1 indicates the register is signed (2 complem.) */
+ uint8_t leng; /*!< number of bits in the register */
+ bool rdon; /*!< 1 indicates a read-only register */
+ int32_t dflt; /*!< register default value */
+};
+
+typedef struct mts {
+ char creset[60];
+ char mtac_hw[60];
+ char spi_path[60];
+ char dev_hw[40];
+ int path;
+} mts_hw;
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
+
+#define MTAC_PAGE_REG 0
+#define MTAC_SOFT_RESET 1
+#define MTAC_VERSION 2
+#define SPI_DEV_PATH_AP1 "/dev/spidev32766.2"
+#define SPI_DEV_PATH_AP2 "/dev/spidev32765.2"
+#define SPI_DEV_PATH_MTCAP "/dev/spidev0.1"
+#define SPI_DEV_PATH_MTCDT "/dev/spidev0.0"
+#define MTCDT "MTAC-LORA-1.5"
+#define MTCAP "MTCAP-LORA-1.5"
+#define HW_FILE "/sys/devices/platform/mts-io/hw-version"
+#define MTCDT_DEFAULT_FILE "mtcdt-fpga-v31.hex"
+#define MTCAP_DEFAULT_FILE "mtcap-fpga-v31.hex"
+#define CRESET "/sys/devices/platform/mts-io/lora/creset"
+#define CRESET_AP1 "/sys/devices/platform/mts-io/ap1/creset"
+#define CRESET_AP2 "/sys/devices/platform/mts-io/ap2/creset"
+#define MTAC_HW_VERSION "/sys/devices/platform/mts-io/lora/hw-version"
+#define MTAC_HW_VERSION_AP1 "/sys/devices/platform/mts-io/ap1/hw-version"
+#define MTAC_HW_VERSION_AP2 "/sys/devices/platform/mts-io/ap2/hw-version"
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
+
+int mtac_get_version(int path);
+
+int mtac_disconnect(void);
+
+int bus_contention(void);
+
+int mtac_reg_w(uint16_t register_id, int32_t reg_value);
+
+int mtac_reg_r(uint16_t register_id, int32_t *reg_value);
+
+int mtac_upgrade(char input_file[255], int path);
+
+int mtac_erase(mts_hw *mts);
+
+int mtac_program(mts_hw *mts, char input_file[]);
+
+int mtac_check(mts_hw *mts);
+
+int hw_check(mts_hw *mts);
+
+void print_supported_versions();
+
+int mtac_creset_write(mts_hw *mts, char num);
+/* --- EOF ------------------------------------------------------------------ */
diff --git a/src/mts_fpga_spi.c b/src/mts_fpga_spi.c
new file mode 100644
index 0000000..1d95ac1
--- /dev/null
+++ b/src/mts_fpga_spi.c
@@ -0,0 +1,513 @@
+#include "mts_error_codes.h"
+#include "mts_fpga_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 MTAC_SPI_ERROR; \
+ }
+#else
+#define DEBUG_MSG(str)
+#define DEBUG_PRINTF(fmt, args...)
+#define CHECK_NULL(a) \
+ if (a == NULL) { \
+ return MTAC_SPI_ERROR; \
+ }
+#endif
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
+
+#define READ_ACCESS 0x00
+#define WRITE_ACCESS 0x80
+#define SPI_SPEED 8000000
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
+
+/*
+ SPI initialization and configuration
+*/
+int mtac_spi_open(char *spidev, void **spi_target_ptr) {
+ int *spi_device = NULL;
+ int dev;
+ int a = 0, b = 0;
+ int i;
+
+ /* check input variables */
+ CHECK_NULL(spi_target_ptr);
+
+ /* allocate memory for the device descriptor */
+ spi_device = malloc(sizeof(int));
+ if (spi_device == NULL) {
+ DEBUG_MSG("ERROR: MALLOC FAIL\n");
+ return MTAC_SPI_OPEN_FAILURE;
+ }
+
+ /* open SPI device */
+ dev = open(spidev, O_RDWR);
+ if (dev < 0) {
+ DEBUG_PRINTF("ERROR: failed to open SPI device %s\n", spidev);
+ return MTAC_SPI_OPEN_FAILURE;
+ }
+
+ /* setting SPI mode to 'mode 0' */
+ i = SPI_MODE_3;
+ a = ioctl(dev, SPI_IOC_WR_MODE, &i);
+ b = ioctl(dev, SPI_IOC_RD_MODE, &i);
+ if ((a < 0) || (b < 0)) {
+ DEBUG_MSG("ERROR: SPI PORT FAIL TO SET IN MODE 0\n");
+ close(dev);
+ free(spi_device);
+ return MTAC_SPI_OPEN_FAILURE;
+ }
+
+ /* setting SPI max clk (in Hz) */
+ i = SPI_SPEED;
+ a = ioctl(dev, SPI_IOC_WR_MAX_SPEED_HZ, &i);
+ b = ioctl(dev, SPI_IOC_RD_MAX_SPEED_HZ, &i);
+ if ((a < 0) || (b < 0)) {
+ DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MAX SPEED\n");
+ close(dev);
+ free(spi_device);
+ return MTAC_SPI_OPEN_FAILURE;
+ }
+
+ /* setting SPI to MSB first */
+ i = 0;
+ a = ioctl(dev, SPI_IOC_WR_LSB_FIRST, &i);
+ b = ioctl(dev, SPI_IOC_RD_LSB_FIRST, &i);
+ if ((a < 0) || (b < 0)) {
+ DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MSB FIRST\n");
+ close(dev);
+ free(spi_device);
+ return MTAC_SPI_OPEN_FAILURE;
+ }
+
+ /* setting SPI to 8 bits per word */
+ i = 0;
+ a = ioctl(dev, SPI_IOC_WR_BITS_PER_WORD, &i);
+ b = ioctl(dev, SPI_IOC_RD_BITS_PER_WORD, &i);
+ if ((a < 0) || (b < 0)) {
+ DEBUG_MSG("ERROR: SPI PORT FAIL TO SET 8 BITS-PER-WORD\n");
+ close(dev);
+ return MTAC_SPI_OPEN_FAILURE;
+ }
+
+ *spi_device = dev;
+ *spi_target_ptr = (void *)spi_device;
+ return MTAC_SUCCESS;
+}
+
+/*
+ Check Chip ID
+*/
+int mtac_id(char *spidev, void *spi_target) {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int ret;
+ size_t command_size = 6;
+ char out_buf[command_size];
+ char in_buf[command_size];
+
+ /* prepare frame to be sent */
+ out_buf[0] = 0x90;
+ out_buf[1] = 0x00;
+ out_buf[2] = 0x00;
+ out_buf[3] = 0x00;
+ out_buf[4] = 0x00;
+ out_buf[5] = 0x00;
+
+ /* I/O transaction */
+ mtac_ret = mtac_spi_open(spidev, &spi_target);
+ spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.rx_buf = (unsigned long)in_buf;
+ k.len = command_size;
+ k.speed_hz = SPI_SPEED;
+ k.cs_change = 0;
+ k.bits_per_word = 8;
+ ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+ mtac_ret = mtac_disconnect();
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ DEBUG_MSG("Chip transfer failed\n");
+ mtac_ret = MTAC_SPI_TRANSFER_FAILURE;
+ }
+ else if (((int)in_buf[4] == 0xFF && (int)in_buf[5] == 0xFF) || ((int)in_buf[4] == 0 && (int)in_buf[5] == 0)) {
+ printf("Unable to communicate with flash\n");
+ mtac_ret = MTAC_SPI_TRANSFER_FAILURE;
+ }
+ else {
+ printf("Manufacturer ID: %x %x",in_buf[4], in_buf[5]);
+ mtac_ret = MTAC_SUCCESS;
+ }
+ return mtac_ret;
+}
+
+/*
+ Chip erase instruction sets all memory within
+ the device to the erased state of all 1s (FFh)
+*/
+int chip_erase(char *spidev, void *spi_target) {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int ret;
+ size_t command_size = 1;
+ char out_buf[command_size];
+ char in_buf[command_size];
+
+ /* prepare frame to be sent */
+ out_buf[0] = CHIP_ERASE;
+
+ /* I/O transaction */
+ mtac_ret = mtac_spi_open(spidev, &spi_target);
+ spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.rx_buf = (unsigned long)in_buf;
+ k.len = command_size;
+ k.speed_hz = SPI_SPEED;
+ k.cs_change = 0;
+ k.bits_per_word = 8;
+ ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+ sleep(5);
+ mtac_ret = mtac_disconnect();
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ DEBUG_MSG("Chip Erase transfer failed\n");
+ mtac_ret = MTAC_SPI_TRANSFER_FAILURE;
+ } else {
+ DEBUG_MSG("Chip Erase transfer successful\n");
+ mtac_erase_verify(spidev, spi_target);
+ mtac_ret = MTAC_SUCCESS;
+ }
+ return mtac_ret;
+}
+
+/*
+ Verify that the chip erase was successful
+*/
+int mtac_erase_verify(char *spidev, void *spi_target) {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int a, ret;
+ uint16_t command_size = 256 + 5;
+ char out_buf[command_size];
+ char in_buf[command_size];
+
+ /* prepare frame to be sent */
+ out_buf[0] = 0x0B;
+ out_buf[1] = 0x00;
+ out_buf[2] = 0x00;
+ out_buf[3] = 0x00;
+ out_buf[4] = 0x00;
+
+ /* I/O transaction */
+ mtac_ret = mtac_spi_open(spidev, &spi_target);
+ spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.rx_buf = (unsigned long)in_buf;
+ k.len = command_size;
+ k.speed_hz = SPI_SPEED;
+ k.cs_change = 1;
+ k.bits_per_word = 8;
+ ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ DEBUG_MSG("Chip transfer failed\n");
+ mtac_ret = MTAC_SPI_TRANSFER_FAILURE;
+ } else {
+ DEBUG_MSG("Chip transfer successful\n");
+ mtac_ret = MTAC_SUCCESS;
+ }
+ mtac_ret = mtac_disconnect();
+ /* verify that the chip was erased */
+ for (a = 5; a < command_size; a++) {
+ if (in_buf[a] != 0xFF) {
+ mtac_disconnect();
+ mtac_ret = MTAC_CHIP_ERASE_FAILURE;
+ return mtac_ret;
+ }
+ }
+ return mtac_ret;
+}
+
+/*
+ Write enable instruction sets the write enable latch
+ in the status register to 1. It needs to be set before
+ a page can be programmed or chip erased
+*/
+int write_enable(char *spidev, void *spi_target) {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int ret;
+ size_t command_size = 1;
+ char out_buf[command_size];
+ char in_buf[command_size];
+
+ /* prepare frame to be sent */
+ out_buf[0] = 0x06;
+
+ /* I/O transaction */
+ mtac_ret = mtac_spi_open(spidev, &spi_target);
+ spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.rx_buf = (unsigned long)in_buf;
+ k.len = command_size;
+ k.speed_hz = SPI_SPEED;
+ k.cs_change = 0;
+ k.bits_per_word = 8;
+ ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+ mtac_ret = mtac_disconnect();
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ DEBUG_MSG("Write Enable failed");
+ mtac_ret = MTAC_SPI_TRANSFER_FAILURE;
+ } else {
+ usleep(1000);
+ DEBUG_MSG("Write Enable successful");
+ mtac_ret = MTAC_SUCCESS;
+ }
+ return mtac_ret;
+}
+
+/*
+ Release Power-down instruction releases the device from
+ said state allowing device communication
+*/
+int mtac_release(char *spidev, void *spi_target) {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int a, ret;
+ size_t command_size = 5;
+ char out_buf[command_size];
+ char in_buf[command_size];
+
+ /* prepare frame to be sent */
+ out_buf[0] = CHIP_RELEASE;
+ out_buf[1] = 0;
+ out_buf[2] = 0;
+ out_buf[3] = 0;
+ out_buf[4] = 0;
+
+ /* I/O transaction */
+ mtac_ret = mtac_spi_open(spidev, &spi_target);
+ spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.rx_buf = (unsigned long)in_buf;
+ k.len = command_size;
+ k.speed_hz = SPI_SPEED;
+ k.cs_change = 1;
+ k.bits_per_word = 8;
+ ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+ mtac_ret = mtac_disconnect();
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ DEBUG_MSG("\nRelease failed\n");
+ mtac_ret = MTAC_SPI_TRANSFER_FAILURE;
+ } else {
+ usleep(1000);
+ DEBUG_MSG("\nRelease successful\n");
+ mtac_ret = MTAC_SUCCESS;
+ }
+ return mtac_ret;
+}
+
+/*
+ Page Program instruction allows writing upto 256 bytes (a page) of
+ data to be programmed at previously erased memory locations Write
+ enable must be issued first
+*/
+int page_program(char *spidev, void *spi_target, uint8_t adr_lower, uint8_t adr_higher,
+ int data[256]) {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int a, ret, h;
+ size_t command_size = 260;
+ char out_buf[command_size];
+ char in_buf[command_size];
+
+ /* prepare frame to be sent */
+ out_buf[0] = PAGE_PROGRAM;
+ out_buf[1] = adr_higher;
+ out_buf[2] = adr_lower;
+ out_buf[3] = 0x00;
+ for (h = 0; h < 256; h++) {
+ out_buf[h + 4] = data[h];
+ }
+
+ /* I/O transaction */
+ mtac_ret = mtac_spi_open(spidev, &spi_target);
+ spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.rx_buf = (unsigned long)in_buf;
+ k.len = command_size;
+ k.speed_hz = SPI_SPEED;
+ k.cs_change = 1;
+ k.bits_per_word = 8;
+ a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+ printf("Writing Page %x%x to MTAC\r", adr_higher, adr_lower);
+ usleep(10000);
+ for (ret = 0; ret < command_size; ret++) {
+ DEBUG_PRINTF("%x ", out_buf[ret]);
+ }
+ mtac_ret = mtac_disconnect();
+
+ /* determine return code */
+ if (a != (int)k.len) {
+ DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");
+ mtac_ret = MTAC_SPI_TRANSFER_FAILURE;
+ } else {
+ DEBUG_MSG("Note: SPI write success\n");
+ mtac_ret = MTAC_SUCCESS;
+ }
+ return mtac_ret;
+}
+
+/* Simple spi write to fpga*/
+int mtac_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target,
+ uint8_t address, uint8_t data) {
+ int spi_device;
+ uint8_t out_buf[3];
+ uint8_t command_size;
+ struct spi_ioc_transfer k;
+ int a;
+
+ /* check input variables */
+ CHECK_NULL(spi_target);
+ if ((address & 0x80) != 0) {
+ DEBUG_MSG("WARNING: SPI address > 127\n");
+ }
+
+ spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */
+
+ /* prepare frame to be sent */
+ if (spi_mux_mode == MTAC_SPI_MUX_MODE1) {
+ out_buf[0] = spi_mux_target;
+ out_buf[1] = WRITE_ACCESS | (address & 0x7F);
+ out_buf[2] = data;
+ command_size = 3;
+ } else {
+ out_buf[0] = WRITE_ACCESS | (address & 0x7F);
+ out_buf[1] = data;
+ command_size = 2;
+ }
+
+ /* I/O transaction */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.len = command_size;
+ k.speed_hz = SPI_SPEED;
+ k.cs_change = 1;
+ k.bits_per_word = 8;
+ a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+
+ /* determine return code */
+ if (a != (int)k.len) {
+ DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");
+ return MTAC_SPI_TRANSFER_FAILURE;
+ } else {
+ DEBUG_MSG("Note: SPI write success\n");
+ return MTAC_SUCCESS;
+ }
+}
+
+/* Simple spi read to fpga */
+int mtac_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target,
+ uint8_t address, uint8_t *data) {
+ int spi_device;
+ uint8_t out_buf[3];
+ uint8_t in_buf[ARRAY_SIZE(out_buf)];
+ uint8_t command_size;
+ struct spi_ioc_transfer k;
+ int a;
+
+ /* check input variables */
+ CHECK_NULL(spi_target);
+ if ((address & 0x80) != 0) {
+ DEBUG_MSG("WARNING: SPI address > 127\n");
+ }
+ CHECK_NULL(data);
+
+ spi_device = *(int *)spi_target; /* spi_target cannot be null beforehand */
+
+ /* prepare frame to be sent */
+ if (spi_mux_mode == MTAC_SPI_MUX_MODE1) {
+ out_buf[0] = spi_mux_target;
+ out_buf[1] = READ_ACCESS | (address & 0x7F);
+ out_buf[2] = 0x00;
+ command_size = 3;
+ } else {
+ out_buf[0] = READ_ACCESS | (address & 0x7F);
+ out_buf[1] = 0x00;
+ command_size = 2;
+ }
+
+ /* I/O transaction */
+ memset(&k, 0, sizeof(k)); /* clear k */
+ k.tx_buf = (unsigned long)out_buf;
+ k.rx_buf = (unsigned long)in_buf;
+ k.len = command_size;
+ k.cs_change = 1;
+ a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
+
+ /* determine return code */
+ if (a != (int)k.len) {
+ DEBUG_MSG("ERROR: SPI READ FAILURE\n");
+ return MTAC_SPI_TRANSFER_FAILURE;
+ } else {
+ DEBUG_MSG("Note: SPI read success\n");
+ *data = in_buf[command_size - 1];
+ return MTAC_SUCCESS;
+ }
+}
+
+/*
+ SPI release
+*/
+int mtac_spi_close(void *spi_target) {
+ int spi_device;
+ int a;
+
+ /* check input variables */
+ CHECK_NULL(spi_target);
+
+ /* close file & deallocate file descriptor */
+ spi_device = *(int *)spi_target; /* check that spi_target is not null */
+ a = close(spi_device);
+ free(spi_target);
+
+ /* determine return code */
+ if (a < 0) {
+ DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n");
+ return MTAC_SPI_CLOSE_FAILURE;
+ } else {
+ DEBUG_MSG("Note: SPI port closed\n");
+ return MTAC_SUCCESS;
+ }
+} \ No newline at end of file
diff --git a/src/mts_fpga_spi.h b/src/mts_fpga_spi.h
new file mode 100644
index 0000000..bf1a54a
--- /dev/null
+++ b/src/mts_fpga_spi.h
@@ -0,0 +1,68 @@
+/* -------------------------------------------------------------------------- */
+/* --- DEPENDANCIES --------------------------------------------------------- */
+
+#include <stdint.h> /* C99 types*/
+#include <fcntl.h> /* open */
+#include <linux/spi/spidev.h>
+#include <stdio.h> /* printf fprintf */
+#include <stdlib.h> /* malloc free */
+#include <string.h> /* memset */
+#include <sys/ioctl.h>
+#include <unistd.h> /* lseek, close */
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC CONSTANTS ----------------------------------------------------- */
+
+#define MTAC_BURST_CHUNK 1024
+
+#define MTAC_SPI_MUX_MODE0 0x0 /* No FPGA */
+#define MTAC_SPI_MUX_MODE1 0x1 /* FPGA, with spi mux header */
+
+#define MTAC_SX1301 0x0
+#define MTAC_FPGA 0x1
+#define MTAC_EEPROM 0x2
+#define MTAC_SX127X 0x3
+
+/* -------------------------------------------------------------------------- */
+/* --- Flash opcodes ---------------------------------------------------------*/
+#define WR_STATUS_REG 0x01 /* Write Status Register */
+#define PAGE_PROGRAM 0x02 /* Write up to a Page of the Memory */
+#define READ_DATA 0x03 /* Read from the Memory */
+#define WRITE_DISABLE 0x04 /* Disable Writing to the Memory */
+#define RD_STATUS_REG_1 0x05 /* Read Status Register-1 */
+#define WRITE_ENABLE 0x06 /* Enable Writing to the Memory */
+#define FAST_READ_DATA 0x0B /* Fast Read from the Memory */
+#define SECTOR_ERASE 0x20 /* Erase a Sector (4kb) */
+#define RD_STATUS_REG_2 0x35 /* Read Status Register-2 */
+#define UNIQUE_ID 0x4B /* Read Unique ID */
+#define WE_STATUS_REG 0x50 /* Write Enable for Status Registers */
+#define BLOCK_ERASE_32 0x52 /* Erase a Block (32kb) */
+#define CHIP_ERASE 0x60 /* Erase Entire Chip */
+#define MNFTR_DEV_ID 0x90 /* Read Manufacturer ID followed by Device ID */
+#define JEDEC_ID 0x9F /* Read JEDEC ID */
+#define CHIP_RELEASE 0xAB /* Release chip from power down */
+#define BLOCK_ERASE_64 0xD8 /* Erase a Block (64kb) */
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */
+
+int mtac_spi_open(char *spidev, void **spi_target_ptr);
+
+int mtac_spi_close(void *spi_target);
+
+int mtac_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target,
+ uint8_t address, uint8_t data);
+
+int mtac_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target,
+ uint8_t address, uint8_t *data);
+
+int chip_erase(char *spidev, void *spi_target);
+
+int mtac_release(char *spidev, void *spi_target);
+
+int page_program(char *spidev, void *spi_target, uint8_t adr_lower, uint8_t adr_higher,
+ int data[256]);
+
+int write_enable(char *spidev, void *spi_target);
+
+int mtac_id(char *spidev, void *spi_target);
+
+/* --- EOF ------------------------------------------------------------------ */