/* -------------------------------------------------------------------------- */ /* --- DEPENDANCIES --------------------------------------------------------- */ #include #include /* open */ #include /* malloc free */ #include /* 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, 35, 37}; 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, "MTCDT3")) { if (mts->path == 0) { strcpy(mts->mtac_hw, MTCDT3_HW_VERSION_AP1); strcpy(mts->spi_path, SPI_DEV_PATH_MTCDT3_AP1); strcpy(mts->creset, MTCDT3_CRESET_AP1); return MTAC_SUCCESS; } else if (mts->path == 1) { strcpy(mts->mtac_hw, MTCDT3_HW_VERSION_AP1); strcpy(mts->spi_path, SPI_DEV_PATH_MTCDT3_AP1); strcpy(mts->creset, MTCDT3_CRESET_AP1); return MTAC_SUCCESS; } else if (mts->path == 2) { strcpy(mts->mtac_hw, MTCDT3_HW_VERSION_AP2); strcpy(mts->spi_path, SPI_DEV_PATH_MTCDT3_AP2); strcpy(mts->creset, MTCDT3_CRESET_AP2); 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 supported 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; } }