summaryrefslogtreecommitdiff
path: root/src/mts_fpga_reg.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mts_fpga_reg.c')
-rw-r--r--src/mts_fpga_reg.c406
1 files changed, 406 insertions, 0 deletions
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