diff options
Diffstat (limited to 'src/mts_fpga_reg.c')
-rw-r--r-- | src/mts_fpga_reg.c | 406 |
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 |