summaryrefslogtreecommitdiff
path: root/src/AccessoryCards/Mtac15Fpga.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/AccessoryCards/Mtac15Fpga.cpp')
-rw-r--r--src/AccessoryCards/Mtac15Fpga.cpp803
1 files changed, 803 insertions, 0 deletions
diff --git a/src/AccessoryCards/Mtac15Fpga.cpp b/src/AccessoryCards/Mtac15Fpga.cpp
new file mode 100644
index 0000000..151ff77
--- /dev/null
+++ b/src/AccessoryCards/Mtac15Fpga.cpp
@@ -0,0 +1,803 @@
+/**********************************************************************
+ * COPYRIGHT 2020 MULTI-TECH SYSTEMS, INC.
+ *
+ * ALL RIGHTS RESERVED BY AND FOR THE EXCLUSIVE BENEFIT OF
+ * MULTI-TECH SYSTEMS, INC.
+ *
+ * MULTI-TECH SYSTEMS, INC. - CONFIDENTIAL AND PROPRIETARY
+ * INFORMATION AND/OR TRADE SECRET.
+ *
+ * NOTICE: ALL CODE, PROGRAM, INFORMATION, SCRIPT, INSTRUCTION,
+ * DATA, AND COMMENT HEREIN IS AND SHALL REMAIN THE CONFIDENTIAL
+ * INFORMATION AND PROPERTY OF MULTI-TECH SYSTEMS, INC.
+ * USE AND DISCLOSURE THEREOF, EXCEPT AS STRICTLY AUTHORIZED IN A
+ * WRITTEN AGREEMENT SIGNED BY MULTI-TECH SYSTEMS, INC. IS PROHIBITED.
+ *
+ ***********************************************************************/
+
+#include "Fpga.h"
+
+/* -------------------------------------------------------------------------- */
+/* --- 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) */ u
+#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) */
+
+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 CONSTANTS ---------------------------------------------------- */
+
+static const char
+ *valid_hashes[3][4] =
+ {
+ {
+ "d9f811fcab57947db3c2323242885a32a7f095a069d3386a148466e7f3da53"
+ "53", /* mtcdt v28*/
+ "903c1199df46d38683b1aa9fc88310abe2f317c01c3aefa77987990874aba4"
+ "20", /* mtcdt v31*/
+ "7c190506b969aea6198daffb6c9b47685f3a4dc3ce18565c66542bac27d6f2"
+ "4e", /* mtcdt v33*/
+ "72bcdfda72bf8677d585330caa3d609615d08d4ca6d7951f0ebbcb5a93306b"
+ "3c" /* mtcdt v35*/
+ },
+ {
+ "54e41b186b2c91f1bcf249648c50357165d361101fc4fe20ee9b8f0c40dce2"
+ "5d" /* mtcdt3 v35*/
+ },
+ {
+ "07317fe9ca59393c074215c9d923d8d01025654883291a5e89b27d21668e22"
+ "63", /* mtcap v28*/
+ "f208ef5cae03e703951bb8799172a5eaadb74ddb90bf3e65c32030c008a88e"
+ "75", /* mtcap v31*/
+ "aaecd468b187703dbbf76022b00268dba2a5f25300da6486d420f476c83638"
+ "5c", /* mtcap v33*/
+ "876cc5683f612c09f96bacb27fff170358c90f3bd76a5c61ec41504eabba83"
+ "13" /* mtcap v35*/
+ },
+};
+
+/* -------------------------------------------------------------------------- */
+/* --- PRIVATE FUNCTIONS DEFINITION ------------------------------------------
+ */
+
+/* hash outputBuffer */
+void Mtac15Fpga::sha256_hash_string(unsigned 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;
+ printInfo("OutputBuffer hash: %s", outputBuffer);
+}
+
+/* Initialize, update and finalize sha256 */
+void Mtac15Fpga::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;
+ printInfo("OutputBuffer finalized: %s", outputBuffer);
+}
+
+/* Open input file and verify sha256 with verified list */
+int Mtac15Fpga::sha256_file(const char *path) {
+ printInfo("Checking hash on input file: %s", path);
+
+ FILE *file = fopen(path, "rb");
+ if (!file) {
+ printError("File %s not found", path);
+ return -1;
+ } else {
+ printInfo("Checking file %s", path);
+ }
+
+ unsigned int i;
+ char file_hash[65];
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ const int bufSize = 32768;
+ unsigned char *buffer = (unsigned char *)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);
+
+ printInfo("Calculated input file hash: %s", file_hash);
+ for (i = 0; i < sizeof(valid_hashes[hardwareType]) /
+ sizeof(valid_hashes[hardwareType][0]);
+ ++i) {
+ if (!strcmp(valid_hashes[hardwareType][i], file_hash)) {
+ printInfo("File verified");
+ return 0;
+ }
+ }
+ printError("Invalid input file");
+ return -1;
+}
+
+int Mtac15Fpga::spiOpen() {
+ int *spi_device = NULL;
+ int dev;
+ int a = 0, b = 0;
+ int i;
+
+ /* allocate memory for the device descriptor */
+ spi_device = (int *)malloc(sizeof(int));
+ if (spi_device == NULL) {
+ printError("Malloc failed");
+ return -1;
+ }
+
+ /* open SPI device */
+ dev = open(spiPath.c_str(), O_RDWR);
+ if (dev < 0) {
+ printError("Failed to open SPI device %s", spiPath.c_str());
+ return -1;
+ }
+
+ /* 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)) {
+ printError("SPI port failed to set IOC MODE 0");
+ close(dev);
+ free(spi_device);
+ return -1;
+ }
+
+ /* 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)) {
+ printError("SPI port failed to set MAX SPEED");
+ close(dev);
+ free(spi_device);
+ return -1;
+ }
+
+ /* 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)) {
+ printError("SPI port failed to set MSB FIRST");
+ close(dev);
+ free(spi_device);
+ return -1;
+ }
+
+ /* 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)) {
+ printError("SPI port failed to set 8 bits-per-word");
+ close(dev);
+ return -1;
+ }
+
+ *spi_device = dev;
+ spi_target_ptr = (void *)spi_device;
+ return 0;
+}
+
+int Mtac15Fpga::spiRead(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;
+
+ spi_device =
+ *(int *)spi_target_ptr; /* spi_target cannot be null beforehand */
+
+ /* prepare frame to be sent */
+ out_buf[0] = spi_mux_target;
+ out_buf[1] = READ_ACCESS | (address & 0x7F);
+ out_buf[2] = 0x00;
+ command_size = 3;
+
+ /* 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) {
+ printError("SPI read failure");
+ return -1;
+ } else {
+ *data = in_buf[command_size - 1];
+ return 0;
+ }
+}
+
+/* Simple spi write to fpga*/
+int Mtac15Fpga::spiWrite(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;
+
+ spi_device =
+ *(int *)spi_target_ptr; /* spi_target cannot be null beforehand */
+
+ /* prepare frame to be sent */
+ out_buf[0] = spi_mux_target;
+ out_buf[1] = WRITE_ACCESS | (address & 0x7F);
+ out_buf[2] = data;
+ command_size = 3;
+
+ /* 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) {
+ printError("SPI write failure");
+ return -1;
+ } else {
+ printDebug("SPI write success");
+ return 0;
+ }
+}
+
+int Mtac15Fpga::spiClose() {
+ int spi_device;
+ int a;
+
+ /* close file & deallocate file descriptor */
+ spi_device = *(int *)spi_target_ptr; /* check that spi_target is not null */
+ a = close(spi_device);
+ free(spi_target_ptr);
+
+ /* determine return code */
+ if (a < 0) {
+ printError("SPI port failed to close");
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/* write to creset pin */
+int Mtac15Fpga::cresetWrite(char num) {
+ std::string cresetPath = SYSFS_PLATFORM + port + CRESET;
+ int fd = open(cresetPath.c_str(), O_WRONLY);
+ if (fd < 0) {
+ printError("Unable to lock file, are you root?");
+ return -1;
+ }
+ write(fd, &num, 1);
+ close(fd);
+ return 0;
+}
+
+/*
+ Release Power-down instruction releases the device from
+ said state allowing device communication
+*/
+int Mtac15Fpga::releaseDevice() {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int 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 = spiOpen();
+ spi_device =
+ *(int *)spi_target_ptr; /* spi_target cannot 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 = spiClose();
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ printError("Release failed");
+ mtac_ret = -1;
+ } else {
+ usleep(1000);
+ mtac_ret = 0;
+ }
+ 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 Mtac15Fpga::writeEnable() {
+ 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 = spiOpen();
+ spi_device =
+ *(int *)spi_target_ptr; /* spi_target cannot 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 = spiClose();
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ printError("Write Enable failed");
+ mtac_ret = -1;
+ } else {
+ usleep(1000);
+ printDebug("Write Enable successful");
+ mtac_ret = 0;
+ }
+ return mtac_ret;
+}
+
+/*
+ Verify that the chip erase was successful
+*/
+int Mtac15Fpga::chipEraseVerify() {
+ 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 = spiOpen();
+ spi_device =
+ *(int *)spi_target_ptr; /* spi_target cannot 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) {
+ printError("Chip transfer failed");
+ mtac_ret = -1;
+ } else {
+ printDebug("Chip transfer successful");
+ mtac_ret = 0;
+ }
+ mtac_ret = spiClose();
+ /* verify that the chip was erased */
+ for (a = 5; a < command_size; a++) {
+ if (in_buf[a] != 0xFF) {
+ mtac_ret = spiClose();
+ return mtac_ret;
+ }
+ }
+ return mtac_ret;
+}
+
+/*
+ Chip erase instruction sets all memory within
+ the device to the erased state of all 1s (FFh)
+*/
+int Mtac15Fpga::chipErase() {
+ 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 = spiOpen();
+ spi_device =
+ *(int *)spi_target_ptr; /* spi_target cannot 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 = spiClose();
+
+ /* determine return code */
+ if (ret != (int)k.len) {
+ printError("Chip Erase transfer failed");
+ mtac_ret = -1;
+ } else {
+ printDebug("Chip Erase transfer successful");
+ chipEraseVerify();
+ mtac_ret = 0;
+ }
+ return mtac_ret;
+}
+
+/* erase entire flash */
+int Mtac15Fpga::mtacErase() {
+ int ret;
+ printInfo("Erasing flash");
+ /* pull device out of powerdown state */
+ ret = releaseDevice();
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* enable writing to flash */
+ ret = writeEnable();
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* send chip erase command to flash */
+ ret = chipErase();
+ if (ret != 0) {
+ return ret;
+ }
+ /* pull device out of powerdown state */
+ ret = releaseDevice();
+ if (ret != 0) {
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ 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 Mtac15Fpga::pageProgram(uint8_t adr_lower, uint8_t adr_higher,
+ uint32_t data[256]) {
+ int mtac_ret;
+ int spi_device;
+ struct spi_ioc_transfer k;
+ int a, 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 = spiOpen();
+ spi_device =
+ *(int *)spi_target_ptr; /* spi_target cannot 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);
+ printDebug("Writing Page %x%x to MTAC\r", adr_higher, adr_lower);
+ usleep(10000);
+
+ mtac_ret = spiClose();
+
+ /* determine return code */
+ if (a != (int)k.len) {
+ printError("SPI write failure");
+ mtac_ret = -1;
+ } else {
+ printDebug("SPI write success");
+ mtac_ret = 0;
+ }
+ return mtac_ret;
+}
+
+/* write to mtac card with input file */
+int Mtac15Fpga::mtacProgram(const char input_file[]) {
+ FILE *f;
+ struct stat st;
+ size_t file_size;
+ int ret = 0;
+ uint32_t i = 0, index, result, 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 = writeEnable();
+ if (ret != 0) {
+ 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 = pageProgram(adr_lower, adr_higher, page_data);
+ if (ret != 0) {
+ free(p_array);
+ break;
+ }
+ /* release device from page program powerdown */
+ ret = releaseDevice();
+ if (ret != 0) {
+ free(p_array);
+ break;
+ }
+ }
+ free(p_array);
+ return ret;
+}
+
+/* Make sure no device is using the spidev bus to prevent contention*/
+int Mtac15Fpga::busContention() {
+ char number[1024];
+ FILE *f = popen("lsof", "r");
+ while (fgets(number, 1024, f) != NULL) {
+ if (strstr(number, "spidev") != NULL) {
+ printError("The accessory card is being used by another process: "
+ "\n %sPlease "
+ "close all LoRaWAN processes and try again",
+ number);
+ pclose(f);
+ return -1;
+ }
+ }
+ pclose(f);
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
+
+/* Constructor used fpga upgrade utility */
+Mtac15Fpga::Mtac15Fpga(std::string inputFile, std::string forcedPath) {
+ FILE *fp = fopen(DEVICE_INFO_FILE, "r");
+ char buf[0XFFFF];
+ rapidjson::FileReadStream input(fp, buf, sizeof(buf));
+ deviceInfo.ParseStream(input);
+ fclose(fp);
+
+ if (!deviceInfo.HasMember("hardwareVersion")) {
+ printError("%s does not have hardware version info, exiting",
+ DEVICE_INFO_FILE);
+ return;
+ }
+ std::string hwVersion = deviceInfo["hardwareVersion"].GetString();
+
+ if (deviceInfo.HasMember("accessoryCards") &&
+ deviceInfo["accessoryCards"].IsArray() &&
+ deviceInfo["accessoryCards"].Size() > 0) {
+ if (hwVersion.find("MTCDT3") != std::string::npos) {
+ hardwareType = HARDWARE_MTCDT3;
+ } else if (hwVersion.find("MTCDT") != std::string::npos) {
+ hardwareType = HARDWARE_MTCDT;
+ } else {
+ return;
+ }
+ if (inputFile.empty()) {
+ input_file = MTCDT_DEFAULT_FILE;
+ } else {
+ input_file = inputFile;
+ }
+ port = deviceInfo["accessoryCards"][0]["port"].GetString();
+ if (port.back() == '2') {
+ spiPath = "/dev/spidev1.2";
+ } else {
+ spiPath = "/dev/spidev0.2";
+ }
+ getFpgaVersion();
+ printInfo("Current FPGA version: %d", fpgaVersion);
+ } else if (hwVersion.find("MTCAP") != std::string::npos) {
+ hardwareType = HARDWARE_MTCAP;
+ if (inputFile.empty()) {
+ input_file = MTCAP_DEFAULT_FILE;
+ } else {
+ input_file = inputFile;
+ }
+ spiPath = "/dev/spidev0.0";
+ getFpgaVersion();
+ printInfo("Current FPGA version: %d", fpgaVersion);
+ }
+}
+
+/* Constructor used by device_info.json generator */
+Mtac15Fpga::Mtac15Fpga(const std::string path) { spiPath = path; }
+
+int Mtac15Fpga::FpgaVersion() { return fpgaVersion; }
+
+/* Open spi device and get fpga version from register */
+int Mtac15Fpga::getFpgaVersion() {
+ int ret = spiOpen();
+ if (ret != 0) {
+ printError("Could not open SPI port %s", spiPath.c_str());
+ } else {
+ /* detect if the gateway has an FPGA with SPI mux header support */
+ ret = spiRead(MTAC_FPGA, loregs[MTAC_VERSION].addr, &fpgaVersion);
+ if (ret != 0) {
+ printError("Could not read FPGA version");
+ }
+ spiClose();
+ }
+ spi_target_ptr = NULL;
+ return ret;
+}
+
+/* setup and upgrade the mtac card with the file specified */
+int Mtac15Fpga::upgradeFpga() {
+ if (hardwareType == HARDWARE_INVALID) {
+ printError("Invalid hardware");
+ return -1;
+ }
+
+ int ret;
+
+ if (input_file.empty()) {
+ printError("Invalid input file %s", input_file.c_str());
+ return -1;
+ }
+ /* check that no other device is using the bus */
+ ret = busContention();
+ if (ret != 0) {
+ return ret;
+ }
+ /* check MTAC Hardware Compatibility */
+ printInfo("Checking hardware compatibility");
+
+ /* check input file checksum */
+ ret = sha256_file(input_file.c_str());
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* pull creset down to access spi flash */
+ ret = cresetWrite('0');
+ if (ret != 0) {
+ return ret;
+ }
+ sleep(1);
+
+ /* erase chip before flashing new firmware */
+ ret = mtacErase();
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* program user specified firmware */
+ printInfo("Programming flash");
+ ret = mtacProgram(input_file.c_str());
+ if (ret != 0) {
+ return ret;
+ } else {
+ printInfo("Write Complete. Resetting FPGA");
+ }
+
+ /* pull creset up to access FPGA */
+ ret = cresetWrite('1');
+ if (ret != 0) {
+ return ret;
+ }
+ sleep(5);
+ printInfo("Reading New FPGA configuration");
+ ret = getFpgaVersion();
+ if (ret != 0) {
+ return ret;
+ }
+ printInfo("New FPGA version: %d", fpgaVersion);
+ return 0;
+}