diff --git a/.gitignore b/.gitignore index d3344372a..482f0f9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -64,12 +64,13 @@ Firmware/Test\ Sketches/?/* # Executables Compare +File_CRC +File_to_Hex_Data_Array NMEA_Client Read_Map_File RTK_Reset Split_Messages X.509_crt_bundle_bin_to_c -File_to_Hex_Data_Array # Claude workspace metadata **/.claude/ diff --git a/Firmware/RTK_Everywhere/Device_Firmware_Update.ino b/Firmware/RTK_Everywhere/Device_Firmware_Update.ino index d4923f213..1372606cc 100644 --- a/Firmware/RTK_Everywhere/Device_Firmware_Update.ino +++ b/Firmware/RTK_Everywhere/Device_Firmware_Update.ino @@ -17,6 +17,9 @@ const char * dfuStateName[] = "DFUS_DONE", "DFUS_INIT", "DFUS_WAIT_NETWORK", + "DFUS_CSV_OPEN", + "DFUS_CSV_READ", + "DFUS_CSV_CLOSE", "DFUS_GET_DEVICE", "DFUS_GET_NETWORK_FILES", "DFUS_GET_HTTP_FILE_LIST_REQ", @@ -47,7 +50,9 @@ const int dfuStateNameCount = sizeof(dfuStateName) / sizeof(dfuStateName[0]); #define DFU_USER_INPUT_OVERFLOWS_BUFFER -4 #define DFU_USER_INPUT_NOT_A_NUMBER -5 +const char * dfuDashes = "--------------------------------------------------"; const char * dfuEqualSigns = "=================================================="; +const char * dfuSpaces = " "; //---------------------------------------- // Locals @@ -221,6 +226,15 @@ void deviceFirmwareCleanup(DEVICE_FIRMWARE_CTX * ctx) rtkFree(ctx->_saveData, "DFU: ctx->_saveData"); } + // Done with the CSV file + if (ctx->_csvFileData) + { + if (settings.debugFirmwareUpdate) + systemPrintf("Freeing CSV file buffer\r\n"); + rtkFree(ctx->_csvFileData, "CSV file data"); + ctx->_csvFileData = nullptr; + } + // Done with the context if (settings.debugFirmwareUpdate) systemPrintf("Freeing device firmware update context, %d bytes\r\n", sizeof(*ctx)); @@ -236,7 +250,8 @@ void deviceFirmwareClose(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { // Display the CRC if (ctx->_complete) - systemPrintf("CRC: 0x%08x\r\n", ctx->_crc); + systemPrintf("CRC: 0x%08x, %d (0x%08x) bytes\r\n", + ctx->_crc, ctx->_crcBytes, ctx->_crcBytes); // Display complete systemPrintf("%s\r\n", dfuEqualSigns); @@ -255,11 +270,12 @@ void deviceFirmwareClose(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) // Determine if all firmware was written dfuLoopInUpdate = false; - if (ctx->_doAll) + if (ctx->_doAll || ctx->_useCsv) { if (ctx->_complete == false) ctx->_reboot = false; - deviceFirmwareFileListReload(ctx); + if (ctx->_doAll) + deviceFirmwareFileListReload(ctx); deviceFirmwareStateSet(ctx, DFUS_NEXT_DEVICE); } else @@ -331,13 +347,13 @@ void deviceFirmwareCrcClose(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { // Display the statistics deviceFirmwarePerformUpdate(ctx); - systemPrintf("CRC: 0x%08x\r\n", ctx->_crc); + systemPrintf("CRC: 0x%08x, %d (0x%08x) bytes\r\n", + ctx->_crc, ctx->_crcBytes, ctx->_crcBytes); ctx->_crcSave = ctx->_crc; deviceFirmwareStateSet(ctx, DFUS_DEVICE_OPEN_INPUT); } else deviceFirmwareStateSet(ctx, DFUS_NEXT_DEVICE); - ctx->_crc = 0; } //---------------------------------------- @@ -345,7 +361,40 @@ void deviceFirmwareCrcClose(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) //---------------------------------------- void deviceFirmwareCrcOpen(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { + const char * crcString; + uint32_t fileCrc; + size_t fileBytes; + + // Determine if the CRC was specified + if (ctx->_useCsv) + { + // Verify that a file length was specified + fileBytes = deviceFirmwareCsvGetNumber(ctx, "file_bytes"); + if (fileBytes) + { + // Verify that the CRC was specified + crcString = deviceFirmwareCsvLocateField(ctx, ctx->_csvDeviceEntry, "file_crc32"); + if (strlen(crcString) > 0) + { + // Get the CRC + fileCrc = deviceFirmwareCsvGetNumber(ctx, "file_crc32"); + + // Use the CRC32 and file bytes specified by the CSV file + ctx->_crc = fileCrc; + ctx->_crcSave = fileCrc; + ctx->_crcBytes = fileBytes; + + // Display the CRC + systemPrintf("CRC: 0x%08x, %d (0x%08x) bytes\r\n", + ctx->_crc, ctx->_crcBytes, ctx->_crcBytes); + deviceFirmwareStateSet(ctx, DFUS_DEVICE_OPEN_INPUT); + return; + } + } + } + // Give user a hint as to what is taking so long + deviceFirmwareReadInit(ctx, dfuFirmwareData._address, dfuFirmwareData._length); if (settings.debugFirmwareUpdate) { if (ctx->_inputDeviceType == DFU_IDT_NETWORK) @@ -360,6 +409,51 @@ void deviceFirmwareCrcOpen(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) deviceFirmwareStateSet(ctx, DFUS_NEXT_DEVICE); } +//---------------------------------------- +// Determine if the file must be read to compute the CRC +//---------------------------------------- +bool deviceFirmwareCrcMustReadFile(DEVICE_FIRMWARE_CTX * ctx) +{ + const char * crcString; + size_t fileBytes; + uint32_t fileCrc; + + do + { + // Determine if the CRC was specified + if (ctx->_useCsv == false) + // Read the file to determine the CRC + break; + + // Verify that a file length was specified + fileBytes = deviceFirmwareCsvGetNumber(ctx, "file_bytes"); + if (fileBytes == 0) + // Read the file to determine the CRC + break; + + // Verify that the CRC was specified + crcString = deviceFirmwareCsvLocateField(ctx, ctx->_csvDeviceEntry, "file_crc32"); + if (strlen(crcString) == 0) + // Read the flie to determine the crc + break; + + // Get the CRC + fileCrc = (uint32_t)deviceFirmwareCsvGetNumber(ctx, "file_crc32"); + + // Use the CRC32 and file bytes specified by the CSV file + ctx->_crc = fileCrc; + ctx->_crcSave = fileCrc; + ctx->_crcBytes = fileBytes; + + // Display the CRC + systemPrintf("CRC: 0x%08x, %d (0x%08x) bytes\r\n", + ctx->_crc, ctx->_crcBytes, ctx->_crcBytes); + deviceFirmwareStateSet(ctx, DFUS_DEVICE_OPEN_INPUT); + return false; + } while (0); + return ctx->_deviceInfo->_crcNeeded; +} + //---------------------------------------- // Read some firmware data //---------------------------------------- @@ -367,9 +461,6 @@ void deviceFirmwareCrcReadData(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { if (deviceFirmwareRead(ctx, currentMsec, DFUS_CRC_CLOSE)) { - // Compute the CRC across these bytes - ctx->_crc = crc32Compute(ctx->_crc, ctx->_buffer, ctx->_validDataBytes); - // Empty the buffer ctx->_validDataBytes = 0; @@ -379,6 +470,545 @@ void deviceFirmwareCrcReadData(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) } } +//---------------------------------------- +// Build the line array for the CSV file +//---------------------------------------- +void deviceFirmwareCsvBuildLineArray(DEVICE_FIRMWARE_CTX * ctx, + char ** lineArray, + int * columnWidthArray) +{ + char * buffer; + char * bufferEnd; + int fieldCount; + int index; + int lineCount; + size_t length; + + // Zero the column widths + for (index = 0; index < ctx->_csvFieldCount; index++) + columnWidthArray[index] = 0; + + // Add each of the lines to the line array + buffer = (char *)ctx->_csvFileData; + bufferEnd = &buffer[ctx->_csvFileBytes]; + lineCount = 0; + while (buffer < bufferEnd) + { + // Add this line to the array + lineArray[lineCount++] = buffer; + + // Maximize the field width + fieldCount = 0; + while ((buffer < bufferEnd) && (fieldCount < ctx->_csvFieldCount)) + { + length = strlen(buffer); + if (columnWidthArray[fieldCount] < length) + columnWidthArray[fieldCount] = length; + + // Set the next field + buffer += length + 1; + fieldCount += 1; + } + + // Skip over the end of the line + while (*buffer == 0) + buffer += 1; + } +} + +//---------------------------------------- +// Close the CSV file +//---------------------------------------- +void deviceFirmwareCsvClose(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) +{ + char * buffer; + char * bufferEnd; + uint8_t data; + int fieldCount; + int lineCount; + char * lineStart; + bool validFile; + + dfuNetworkCleanup(ctx, nullptr); + validFile = (ctx->_bytesRead == ctx->_fileBytes); + if (validFile) + { + do + { + // Display the statistics + deviceFirmwarePerformUpdate(ctx); + ctx->_fileBytes = 0; + + // Count the number of fields + buffer = (char *)ctx->_csvFileData; + bufferEnd = &buffer[ctx->_csvFileBytes]; + if (settings.debugFirmwareUpdate && ctx->_debugVerbose) + dumpBuffer(0, ctx->_csvFileData, ctx->_csvFileBytes); + ctx->_csvFieldCount = 0; + while ((buffer < bufferEnd) && (*buffer != '\r') && (*buffer != '\n')) + { + if (*buffer == ',') + { + *buffer = 0; + ctx->_csvFieldCount += 1; + } + buffer += 1; + } + ctx->_csvFieldCount += 1; + if (settings.debugFirmwareUpdate && ctx->_debugVerbose) + systemPrintf("ctx->_csvFieldCount: %d\r\n", ctx->_csvFieldCount); + + // Parse the reset of the file + while ((buffer < bufferEnd) && ((*buffer == '\r') || (*buffer == '\n'))) + *buffer++ = 0; + + // Count the number of lines + lineCount = 1; + validFile = true; + while (buffer < bufferEnd) + { + // Count the fields in this line + lineStart = buffer; + fieldCount = 0; + while ((buffer < bufferEnd) && (*buffer != '\r') && (*buffer != '\n')) + { + if (*buffer == ',') + { + *buffer = 0; + fieldCount += 1; + } + buffer += 1; + } + fieldCount += 1; + + // Done with this line + while ((buffer < bufferEnd) && ((*buffer == '\r') || (*buffer == '\n'))) + { + *buffer = 0; + buffer += 1; + } + + // Validate the number of fields + if (fieldCount != ctx->_csvFieldCount) + { + // Display the error + systemPrintf("ERROR: CSV file line %d at offset 0x%08x has %d fields, expected %d fields!\r\n", + lineCount, buffer - lineStart, fieldCount, ctx->_csvFieldCount); + validFile = false; + } + + // Account for this line + lineCount += 1; + } + ctx->_csvLineCount = lineCount; + if (settings.debugFirmwareUpdate && ctx->_debugVerbose) + systemPrintf("ctx->_csvLineCount: %d\r\n", ctx->_csvLineCount); + + // Check for error + if (validFile == false) + break; + + // Display the CSV file contents + if (settings.debugFirmwareUpdate && ctx->_debugVerbose) + { + dumpBuffer(0, ctx->_csvFileData, ctx->_csvFileBytes); + deviceFirmwareCsvDisplay(ctx); + } + + // Reduce the lines to those for the current product + if (deviceFirmwareCsvGetProductLines(ctx) == false) + { + systemPrintf("ERROR: Unable to locate firmware files for %s\r\n", platformPrefix); + validFile = false; + break; + } + } while (0); + } + + // Handle error, failed to read in or parse the CSV file, force last state + if (validFile == false) + { + if (settings.debugFirmwareUpdate && ctx->_debugVerbose) + dumpBuffer(0, ctx->_csvFileData, ctx->_csvFileBytes); + ctx->_deviceInfo = &deviceFirmwareInfo[0]; + } + + // Continue processing + deviceFirmwareStateSet(ctx, DFUS_NEXT_DEVICE); +} + +//---------------------------------------- +// Display the contents of the CSV file +//---------------------------------------- +void deviceFirmwareCsvDisplay(DEVICE_FIRMWARE_CTX * ctx) +{ + char * buffer; + int columnWidth[ctx->_csvFieldCount]; + int countLeft; + int countRight; + int countSpaces; + int index; + char * line[ctx->_csvLineCount]; + int lineNumber; + size_t length; + int width; + + // Locate the lines + deviceFirmwareCsvBuildLineArray(ctx, &line[0], &columnWidth[0]); + + // Display the column widths + if (settings.debugFirmwareUpdate && ctx->_debugVerbose) + { + buffer = (char *)ctx->_csvFileData; + systemPrintf("Column Widths\r\n"); + for (index = 0; index < ctx->_csvFieldCount; index++) + { + systemPrintf(" %2d: %s\r\n", columnWidth[index], buffer); + buffer += strlen(buffer) + 1; + } + } + + // Display the header + deviceFirmwareCsvDisplayHeaderLine(ctx, &columnWidth[0]); + buffer = (char *)ctx->_csvFileData; + countSpaces = strlen(dfuSpaces); + systemPrintf("|"); + for (index = 0; index < ctx->_csvFieldCount; index++) + { + // Center the field name + length = strlen(buffer); + width = 1 + columnWidth[index] + 1; + countRight = width - length; + countLeft = countRight >> 1; + countRight -= countLeft; + systemPrintf("%s%s%s|", + &dfuSpaces[countSpaces - countLeft], + buffer, + &dfuSpaces[countSpaces - countRight]); + buffer += strlen(buffer) + 1; + } + systemPrintln(); + deviceFirmwareCsvDisplayHeaderLine(ctx, &columnWidth[0]); + + // Display each of the lines + for (lineNumber = 1; lineNumber < ctx->_csvLineCount; lineNumber++) + { + // Skip the end-of-line + while (*buffer == 0) + buffer += 1; + + systemPrintf("|"); + for (index = 0; index < ctx->_csvFieldCount; index++) + { + // Left justify the field + length = strlen(buffer); + width = 1 + columnWidth[index] + 1; + countLeft = 0; + countRight = 0; + if ((index < 2) || (index == 7)) + countRight = width - length; + + // Right justify the field + else + countLeft = width - length; + while (countLeft > countSpaces) + { + systemPrint(dfuSpaces); + countLeft -= countSpaces; + } + systemPrintf("%s%s%s|", + &dfuSpaces[countSpaces - countLeft], + buffer, + &dfuSpaces[countSpaces - (countRight % countSpaces)]); + countRight -= countRight % countSpaces; + while (countRight > countSpaces) + { + systemPrint(dfuSpaces); + countLeft -= countRight; + } + + // Set the next field + buffer += strlen(buffer) + 1; + } + systemPrintln(); + deviceFirmwareCsvDisplayHeaderLine(ctx, &columnWidth[0]); + } +} + +//---------------------------------------- +// Display the header line +//---------------------------------------- +void deviceFirmwareCsvDisplayHeaderLine(DEVICE_FIRMWARE_CTX * ctx, int * columnWidth) +{ + char * buffer; + size_t dashesLength; + int index; + int width; + + buffer = (char *)ctx->_csvFileData; + systemPrintf("+"); + dashesLength = strlen(dfuDashes); + for (index = 0; index < ctx->_csvFieldCount; index++) + { + width = 1 + columnWidth[index] + 1; + while (width > dashesLength) + { + systemPrint(dfuDashes); + width -= dashesLength; + } + systemPrintf("%s+", &dfuDashes[dashesLength - width]); + buffer += strlen(buffer) + 1; + } + systemPrintln(); +} + +//---------------------------------------- +// Get the value from the specified field +//---------------------------------------- +int deviceFirmwareCsvGetNumber(DEVICE_FIRMWARE_CTX * ctx, + const char * fieldName) +{ + int value; + const char * string; + + string = deviceFirmwareCsvLocateField(ctx, ctx->_csvDeviceEntry, fieldName); + if (string == nullptr) + return 0; + if (sscanf(string, "0x%x", &value) == 1) + return value; + if (sscanf(string, "%d", &value) == 1) + return value; + return 0; +} + +//---------------------------------------- +// Reduce the CSV file contents to the header line and product lines only +//---------------------------------------- +bool deviceFirmwareCsvGetProductLines(DEVICE_FIRMWARE_CTX * ctx) +{ + char * buffer; + char * bufferEnd; + int fieldIndex; + int lineCount; + int lineIndex; + const char * product; + char * nextLine; + + // Skip over the header line + buffer = (char *)ctx->_csvFileData; + bufferEnd = &buffer[ctx->_csvFileBytes]; + buffer = deviceFirmwareCsvNextLine(ctx, buffer, bufferEnd); + lineCount = 1; + nextLine = buffer; + + // Display the product + product = platformPrefix; + if (settings.debugFirmwareUpdate && ctx->_debugVerbose) + systemPrintf("Looking for product: %s\r\n", product); + + // Locate the platform lines + for (lineIndex = 1; lineIndex < ctx->_csvLineCount; lineIndex++) + { + if (strcmp(product, buffer) != 0) + buffer = deviceFirmwareCsvNextLine(ctx, buffer, bufferEnd); + else + { + // Determine if the line needs to be moved + lineCount += 1; + if (buffer == nextLine) + // No, in correct position + buffer = deviceFirmwareCsvNextLine(ctx, buffer, bufferEnd); + else + { + // Copy the line to the beginning of the buffer + for (fieldIndex = 0; fieldIndex < ctx->_csvFieldCount; fieldIndex++) + { + strcpy(nextLine, buffer); + nextLine += strlen(nextLine) + 1; + buffer += strlen(buffer) + 1; + } + while (*buffer == 0) + { + *nextLine++ = 0; + buffer += 1; + } + } + } + } + + // Update the CSV contents + if (lineCount > 1) + { + ctx->_csvLineCount = lineCount; + ctx->_csvFileBytes = nextLine - (char *)ctx->_csvFileData; + } + + // Display the entries in the table + deviceFirmwareCsvDisplay(ctx); + return (lineCount > 1); +} + +//---------------------------------------- +// Locate a device entry in the CSV file +//---------------------------------------- +const char * deviceFirmwareCsvLocateDevice(DEVICE_FIRMWARE_CTX * ctx) +{ + const char * bufferEnd; + const char * csvEntry; + const char * subsystem; + + // Locate the device in the CSV file + csvEntry = (const char *)ctx->_csvFileData; + bufferEnd = &csvEntry[ctx->_csvFileBytes]; + for (int index = 1; index < ctx->_csvLineCount; index++) + { + // Skip to the next entry + csvEntry = deviceFirmwareCsvNextLine(ctx, (char *)csvEntry, bufferEnd); + + // Locate the field + subsystem = deviceFirmwareCsvLocateField(ctx, csvEntry, "subsystem"); + if (subsystem == nullptr) + reportFatalError("Failed to locate subsystem field in CSV file!\r\n"); + + // Determine if this is the correct subsystem + if (strcmp(subsystem, ctx->_deviceInfo->_deviceName) == 0) + return csvEntry; + } + + // No matching entry + return nullptr; +} + +//---------------------------------------- +// Locate a field in an entry in the CSV file +//---------------------------------------- +const char * deviceFirmwareCsvLocateField(DEVICE_FIRMWARE_CTX * ctx, + const char * csvEntry, + const char * fieldName) +{ + const char * buffer; + int columnNumber; + const char * field; + + // Locate the field name + field = (const char *)ctx->_csvFileData; + for (columnNumber = 0; columnNumber < ctx->_csvFieldCount; columnNumber++) + { + if (strcmp(field, fieldName) == 0) + break; + field += strlen(field) + 1; + } + + // Handle the error when the fieldName does not match any fields in the file + if (columnNumber >= ctx->_csvFieldCount) + return nullptr; + + // Locate the specific field + for (int index = 0; index < columnNumber; index++) + csvEntry += strlen(csvEntry) + 1; + return csvEntry; +} + +//---------------------------------------- +// Set the next line in the CSV file +//---------------------------------------- +char * deviceFirmwareCsvNextLine(DEVICE_FIRMWARE_CTX * ctx, + char * buffer, + const char * bufferEnd) +{ + // Skip over the fields in the current line + for (int index = 0; (index < ctx->_csvFieldCount) && (buffer < bufferEnd); index++) + { + buffer += strlen(buffer) + 1; + } + + // Skip over any extra zero's for \r or \n + while (*buffer == 0) + buffer += 1; + return buffer; +} + +//---------------------------------------- +// Open the CSV file and allocate the CSV buffer +//---------------------------------------- +void deviceFirmwareCsvOpen(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) +{ + do + { + // Open the CSV file + if (deviceFirmwareOpenUrl(ctx, currentMsec) == false) + break; + + // Allocate space for the CSV file + ctx->_csvFileBytes = ctx->_fileBytes; + if (settings.debugFirmwareUpdate) + systemPrintf("Allocating CSV file buffer, %d bytes\r\n", ctx->_csvFileBytes); + ctx->_csvFileData = (uint8_t *)rtkMalloc(ctx->_csvFileBytes, "CSV file data"); + if (ctx->_csvFileData == nullptr) + { + systemPrintf("ERROR: Failed to allocate the CSV file buffer, %d bytes\r\n", ctx->_fileBytes); + break; + } + deviceFirmwareReadInit(ctx, ctx->_csvFileData, ctx->_csvFileBytes); + ctx->_inputDeviceType = DFU_IDT_NETWORK; + deviceFirmwareStateSet(ctx, DFUS_CSV_READ); + return; + } while (0); + + // Failed to open or parse the CSV file, force last state + ctx->_deviceInfo = &deviceFirmwareInfo[0]; + deviceFirmwareStateSet(ctx, DFUS_NEXT_DEVICE); +} + +//---------------------------------------- +// Read the CSV file data into the CSV buffer +//---------------------------------------- +void deviceFirmwareCsvRead(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) +{ + if (deviceFirmwareRead(ctx, currentMsec, DFUS_CSV_CLOSE)) + { + // Empty the buffer + ctx->_validDataBytes = 0; + + // Wait until done + if (ctx->_bytesRead == ctx->_csvFileBytes) + deviceFirmwareStateSet(ctx, DFUS_CSV_CLOSE); + } +} + +//---------------------------------------- +// Does the device need an update +//---------------------------------------- +bool deviceFirmwareCsvVersionNeedsUpdating(DEVICE_FIRMWARE_CTX * ctx) +{ + const DEVICE_FIRMWARE_INFO * deviceInfo; + int major; + int minor; + int patch; + int releaseCandidate; + int revision; + + // Get the values from the CSV file + major = deviceFirmwareCsvGetNumber(ctx, "version_major"); + minor = deviceFirmwareCsvGetNumber(ctx, "version_minor"); + patch = deviceFirmwareCsvGetNumber(ctx, "version_patch"); + revision = deviceFirmwareCsvGetNumber(ctx, "version_revision"); + releaseCandidate = deviceFirmwareCsvGetNumber(ctx, "release_candidate"); + + // Determine if the device need updating + deviceInfo = ctx->_deviceInfo; + + // Always upgrade if comparison routine is not specified + // Always upgrade if version does not match the CSV specified version + return ((deviceInfo->_cmpVersion == nullptr) + || (deviceInfo->_cmpVersion(ctx, + major, + minor, + patch, + revision, + releaseCandidate) != 0)); +} + //---------------------------------------- // Determine if the device is available for firmware updates //---------------------------------------- @@ -475,7 +1105,7 @@ void deviceFirmwareFileListMenu(DEVICE_FIRMWARE_CTX * ctx) // Display the firmware version if (ctx->_deviceInfo->_version) - systemPrintf("\r\nCurrent firmware version: %s\r\n", ctx->_deviceInfo->_version(ctx).c_str()); + systemPrintf("\r\nCurrent firmware version: %d\r\n", ctx->_deviceInfo->_version(ctx)); // Display the files offset = 0; @@ -725,7 +1355,11 @@ void deviceFirmwareNextDevice(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) // Done with the web server if (ctx->_https) { - ctx->_networkClient; + if (ctx->_networkClient) + { + ctx->_networkClient->stop(); + ctx->_networkClient = nullptr; + } ctx->_https->end(); delete ctx->_https; ctx->_https = nullptr; @@ -749,8 +1383,8 @@ void deviceFirmwareNextDevice(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) } // Handle the next device - deviceFirmwareStateSet(ctx, ctx->_doAll ? DFUS_GET_DEVICE : - (ctx->_reboot ? DFUS_REBOOT : DFUS_DONE)); + deviceFirmwareStateSet(ctx, (ctx->_doAll || ctx->_useCsv) ? DFUS_GET_DEVICE : + (ctx->_reboot ? DFUS_REBOOT : DFUS_DONE)); } //---------------------------------------- @@ -788,7 +1422,7 @@ bool deviceFirmwareOpenInput(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) if (ctx->_outputDeviceType == DFU_ODT_NONE) reportFatalError("Output device type is DFU_ODT_NONE!"); - ctx->_bytesRead = 0; + deviceFirmwareReadInit(ctx, dfuFirmwareData._address, dfuFirmwareData._length); ctx->_complete = false; ctx->_startMsec = millis(); @@ -823,6 +1457,7 @@ void deviceFirmwareOpenOutput(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { ctx->_bytesWritten = 0; ctx->_packetNumber = 0; + ctx->_percentage = -1; result = false; // Network write data path @@ -888,6 +1523,8 @@ void deviceFirmwareOpenOutput(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) bool deviceFirmwareOpenUrl(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { int attempt; + const char * crcString; + size_t fileBytes; int httpResponseCode; if (settings.debugFirmwareUpdate) @@ -923,6 +1560,26 @@ bool deviceFirmwareOpenUrl(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) if (settings.debugFirmwareUpdate) systemPrintf("File size: %d bytes\r\n", ctx->_fileBytes); + // Determine if the file size is valid + if (ctx->_useCsv && ctx->_csvDeviceEntry) + { + fileBytes = deviceFirmwareCsvGetNumber(ctx, "file_bytes"); + if (fileBytes != ctx->_fileBytes) + { + systemPrintf("CSV File size does not match, CSV bytes: %d, Actual bytes: %d\r\n", + fileBytes, ctx->_fileBytes); + return false; + } + + // Verify that the CRC string was specified + crcString = deviceFirmwareCsvLocateField(ctx, ctx->_csvDeviceEntry, "file_crc32"); + if (strlen(crcString) == 0) + { + systemPrintf("CVS file missing CRC32 value!\r\n"); + return false; + } + } + // Get TCP stream ctx->_networkClient = ctx->_https->getStreamPtr(); return true; @@ -994,8 +1651,11 @@ bool deviceFirmwareRead(DEVICE_FIRMWARE_CTX * ctx, return false; } else if (bytesRead) + { // Compute the CRC ctx->_crc = crc32Compute(ctx->_crc, ctx->_data, bytesRead); + ctx->_crcBytes += bytesRead; + } } // Remaining data starts at the beginning of the buffer @@ -1034,6 +1694,22 @@ void deviceFirmwareReadFillBuffer(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMse { if (deviceFirmwareRead(ctx, currentMsec, DFUS_DEVICE_CLOSE)) { + // Verify the file CRC + if (ctx->_bytesRead == ctx->_fileBytes) + { + if (ctx->_useCsv || ctx->_deviceInfo->_crcNeeded) + { + if (ctx->_crc != ctx->_crcSave) + { + systemPrintf("CRC verification failed, expected: 0x%08x, actual: 0x%08x\r\n", + ctx->_crcSave, ctx->_crc); + deviceFirmwareStateSet(ctx, DFUS_DEVICE_CLOSE); + return; + } + } + } + + // Start the firmware update processing if (ctx->_outputDeviceType == DFU_ODT_DEVICE) deviceFirmwareStopTasks(ctx); if (ctx->_doAll == false) @@ -1047,8 +1723,43 @@ void deviceFirmwareReadFillBuffer(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMse //---------------------------------------- void deviceFirmwareReadFirmwareData(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { - if (deviceFirmwareRead(ctx, currentMsec, DFUS_CRC_CLOSE)) + // Read more firmware data into the buffer + if (deviceFirmwareRead(ctx, currentMsec, DFUS_DEVICE_CLOSE)) + { + // Verify the file CRC + if (ctx->_bytesRead == ctx->_fileBytes) + { + // The last few bytes are in the buffer and need to be programmed + if (ctx->_useCsv || ctx->_deviceInfo->_crcNeeded) + { + // The expected CRC is available, verify it + if (ctx->_crc != ctx->_crcSave) + { + systemPrintf("\r\nCRC verification failed, expected: 0x%08x, actual: 0x%08x\r\n", + ctx->_crcSave, ctx->_crc); + deviceFirmwareStateSet(ctx, DFUS_DEVICE_CLOSE); + return; + } + } + } + + // More firmware to program deviceFirmwareStateSet(ctx, DFUS_DEVICE_PROGRAM_FIRMWARE); + } +} + +//---------------------------------------- +// Initialize the read operation +//---------------------------------------- +void deviceFirmwareReadInit(DEVICE_FIRMWARE_CTX * ctx, uint8_t * buffer, size_t length) +{ + ctx->_buffer = buffer; + ctx->_data = buffer; + ctx->_bufferLength = length; + ctx->_bytesRead = 0; + ctx->_crc = 0; + ctx->_crcBytes = 0; + ctx->_validDataBytes = 0; } //---------------------------------------- @@ -1187,11 +1898,11 @@ void deviceFirmwareSelectAction(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) // Are all the devices being updated? deviceInfo = ctx->_deviceInfo; - if (ctx->_doAll) + if (ctx->_doAll || ctx->_useCsv) { // Performing the firmware update ctx->_outputDeviceType = DFU_ODT_DEVICE; - deviceFirmwareStateSet(ctx, ctx->_deviceInfo->_crcNeeded + deviceFirmwareStateSet(ctx, deviceFirmwareCrcMustReadFile(ctx) ? DFUS_CRC_OPEN_INPUT : DFUS_DEVICE_OPEN_INPUT); break; @@ -1264,7 +1975,7 @@ void deviceFirmwareSelectAction(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) // Start the programming process ctx->_outputDeviceType = DFU_ODT_DEVICE; - deviceFirmwareStateSet(ctx, ctx->_deviceInfo->_crcNeeded + deviceFirmwareStateSet(ctx, deviceFirmwareCrcMustReadFile(ctx) ? DFUS_CRC_OPEN_INPUT : DFUS_DEVICE_OPEN_INPUT); } @@ -1283,8 +1994,11 @@ void deviceFirmwareSelectAction(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) //---------------------------------------- void deviceFirmwareSelectDevice(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) { + const DEVICE_FIRMWARE_INFO * deviceInfo; + const char * fileName; int incoming; size_t length; + String url; do { @@ -1312,8 +2026,9 @@ void deviceFirmwareSelectDevice(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) // Determine if the next device is in the system ctx->_deviceInfo -= 1; - if ((ctx->_deviceInfo->_present == nullptr) - || (*ctx->_deviceInfo->_present)) + deviceInfo = ctx->_deviceInfo; + if ((deviceInfo->_present == nullptr) + || (*deviceInfo->_present)) { if ((dfuBufferInfo[0]._bufferData == nullptr) || (dfuBufferInfo[0]._bufferData->_address == nullptr)) @@ -1324,14 +2039,79 @@ void deviceFirmwareSelectDevice(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) } // Display the firmware version - if (ctx->_deviceInfo->_version) - systemPrintf("Current firmware version: %s\r\n", ctx->_deviceInfo->_version(ctx).c_str()); + if (deviceInfo->_version) + systemPrintf("Current firmware version: %d\r\n", deviceInfo->_version(ctx)); // Program the next device goto nextDevice; } } } + else if (ctx->_useCsv) + { + // Start from the end of the device list + if (ctx->_deviceInfo == nullptr) + ctx->_deviceInfo = &deviceFirmwareInfo[deviceFirmwareInfoCount]; + + // Locate the next device + while (1) + { + // Determine if it is time to reboot + if (ctx->_deviceInfo == &deviceFirmwareInfo[0]) + { + if (ctx->_reboot) + dfuEsp32Reboot(); + + // Display the firmware update failure + systemPrintf("%s\r\n", dfuEqualSigns); + systemPrintf("HALTED: Firmware update failed!\r\n"); + systemPrintf("%s\r\n", dfuEqualSigns); + reportFatalError("Firmware update failed!"); + } + + // Determine if the next device is in the system + ctx->_deviceInfo -= 1; + deviceInfo = ctx->_deviceInfo; + if ((deviceInfo->_present == nullptr) + || (*deviceInfo->_present)) + { + // Attempt to locate this device in the CSV file + ctx->_csvDeviceEntry = deviceFirmwareCsvLocateDevice(ctx); + if (ctx->_csvDeviceEntry) + { + // Verify the version + if (deviceFirmwareCsvVersionNeedsUpdating(ctx)) + { + // Determine if the read buffer has been allocated + if ((dfuBufferInfo[0]._bufferData == nullptr) + || (dfuBufferInfo[0]._bufferData->_address == nullptr)) + { + // Allocate the buffers + if (deviceFirmwareBufferAllocate(ctx) == false) + reportFatalError("Failed buffer allocation!"); + } + + // Display the firmware version + if (deviceInfo->_version) + systemPrintf("Current firmware version: %d\r\n", deviceInfo->_version(ctx)); + + // Program the next device + goto nextDevice; + } + + // No need to update, already at the correct version + else + { + const char * separation = &dfuEqualSigns[strlen(dfuEqualSigns) - 40]; + + systemPrintf("%s\r\n", separation); + systemPrintf("%s is already up-to-date\r\n", deviceInfo->_deviceName); + systemPrintf("%s\r\n", separation); + } + } + } + } + } // Handle the menu timeout incoming = deviceFirmwareGetNumber(ctx, currentMsec); @@ -1357,10 +2137,11 @@ void deviceFirmwareSelectDevice(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) nextDevice: // Display the menu choice - systemPrintf("Selected device: %s\r\n", ctx->_deviceInfo->_deviceName); + deviceInfo = ctx->_deviceInfo; + systemPrintf("Selected device: %s\r\n", deviceInfo->_deviceName); // Allocate the necessary write buffer - length = ctx->_deviceInfo->_writeBufferBytes; + length = deviceInfo->_writeBufferBytes; if (length) { // Allocate the write buffer for this device @@ -1377,7 +2158,7 @@ nextDevice: } // Allocate the device specific context - length = ctx->_deviceInfo->_devContextBytes; + length = deviceInfo->_devContextBytes; if (length) { // Allocate the device specific context @@ -1394,12 +2175,12 @@ nextDevice: // Initialize the device specific context memset(ctx->_devCtx, 0, length); - if (ctx->_deviceInfo->_initDevCtx) + if (deviceInfo->_initDevCtx) { - if (ctx->_deviceInfo->_initDevCtx(ctx) == false) + if (deviceInfo->_initDevCtx(ctx) == false) { systemPrintf("ERROR: Failed to initialize the %s device specific context\r\n", - ctx->_deviceInfo->_deviceName); + deviceInfo->_deviceName); if (ctx->_doAll) ctx->_reboot = false; deviceFirmwareStateSet(ctx, DFUS_NEXT_DEVICE); @@ -1408,8 +2189,41 @@ nextDevice: } // Determine maximum bytes to write at a time - ctx->_bytesMax = ctx->_deviceInfo->_maxWriteBytes - ? ctx->_deviceInfo->_maxWriteBytes : ctx->_bufferLength; + ctx->_bytesMax = deviceInfo->_maxWriteBytes + ? deviceInfo->_maxWriteBytes : ctx->_bufferLength; + + // When using the CSV file, build the URL and start the device programming + if (ctx->_useCsv) + { + // Get the file_path + fileName = deviceFirmwareCsvLocateField(ctx, ctx->_csvDeviceEntry, "file_name"); + if (fileName == nullptr) + { + systemPrintf("ERROR: Unable to locate file_path in CSV file for %s\r\n", ctx->_deviceInfo->_deviceName); + deviceInfo = &deviceFirmwareInfo[0]; + deviceFirmwareStateSet(ctx, DFUS_NEXT_DEVICE); + break; + } + + // Build the file name + ctx->_fileName = "/"; + ctx->_fileName += fileName; + + // Build the raw URL + url = deviceInfo->_server; + if (deviceInfo->_rawBranch) + url += deviceInfo->_rawBranch; + if (deviceInfo->_directory) + url += deviceInfo->_directory; + url += ctx->_fileName; + ctx->_url = url; + ctx->_validDataBytes = 0; + + + // Determine what to do with this file + deviceFirmwareStateSet(ctx, DFUS_SELECT_ACTION); + break; + } // Get the files systemPrintf("Getting the file list...\r\n"); @@ -1635,6 +2449,9 @@ bool deviceFirmwareUpdate(uint32_t currentMsec) { case DFUS_INIT: deviceFirmwareInit(ctx, currentMsec); break; case DFUS_WAIT_NETWORK: deviceFirmwareWaitForNetwork(ctx, currentMsec); break; + case DFUS_CSV_OPEN: deviceFirmwareCsvOpen(ctx, currentMsec); break; + case DFUS_CSV_READ: deviceFirmwareCsvRead(ctx, currentMsec); break; + case DFUS_CSV_CLOSE: deviceFirmwareCsvClose(ctx, currentMsec); break; case DFUS_GET_DEVICE: deviceFirmwareSelectDevice(ctx, currentMsec); break; case DFUS_GET_NETWORK_FILES: dfuNetworkFileListBuildUrl(ctx); break; case DFUS_GET_HTTP_FILE_LIST_REQ: dfuNetworkFileListHtmlRequest(ctx, currentMsec); break; @@ -1677,7 +2494,8 @@ bool deviceFirmwareUpdate(uint32_t currentMsec) //---------------------------------------- // State machine to perform the device firmware update //---------------------------------------- -bool deviceFirmwareUpdateBegin(bool doAll, +bool deviceFirmwareUpdateBegin(const char * csvUrl, + bool doAll, bool debugVerbose, size_t saveDataLength) { @@ -1715,6 +2533,12 @@ bool deviceFirmwareUpdateBegin(bool doAll, // Initialize the context memset(ctx, 0, length); ctx->_doAll = doAll; + if (csvUrl) + { + ctx->_useCsv = true; + ctx->_url = String(csvUrl); + ctx->_reboot = true; + } ctx->_debugVerbose = debugVerbose; if (doAll) { @@ -1757,6 +2581,17 @@ void deviceFirmwareVerifyTables() reportFatalError("Fix _DEVICE_FIRMWARE_UPDATE_STATE and dfuStateName!"); } +//---------------------------------------- +// Convert the version number into a string +//---------------------------------------- +String deviceFirmwareVersion(int versionNumber) +{ + char line[32]; + + sprintf(line, "%d", versionNumber); + return String(line); +} + //---------------------------------------- // Wait for a network connection //---------------------------------------- @@ -1787,9 +2622,15 @@ bool deviceFirmwareWaitForNetwork(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMse // Done timing out the network connection ctx->_timerMsec = 0; - // Display the menu - deviceFirmwareStateSet(ctx, DFUS_GET_DEVICE); - deviceFirmwareDeviceListMenu(ctx); + // Determine if CSV file is being used + if (ctx->_useCsv) + deviceFirmwareStateSet(ctx, DFUS_CSV_OPEN); + else + { + // Display the menu + deviceFirmwareStateSet(ctx, DFUS_GET_DEVICE); + deviceFirmwareDeviceListMenu(ctx); + } } while (0); return hasInternetAccess; } @@ -1803,7 +2644,7 @@ void deviceFirmwareWrite(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec) ssize_t bytesWritten; bool done; int percentage; - DEVICE_WRITE write; + DFU_DEVICE_WRITE write; // Determine if there is enough data to write write = ctx->_deviceInfo->_write; diff --git a/Firmware/RTK_Everywhere/Device_Update.h b/Firmware/RTK_Everywhere/Device_Update.h index 72ab1027b..2fc6f01ca 100644 --- a/Firmware/RTK_Everywhere/Device_Update.h +++ b/Firmware/RTK_Everywhere/Device_Update.h @@ -38,6 +38,7 @@ typedef struct _DEVICE_FIRMWARE_CTX void * _devCtx; // Device specific context address const struct _DEVICE_FIRMWARE_INFO * _deviceInfo; // Selected device int _state; // Current device firmware update state + bool _useCsv; // Use CSV file to select firmware files bool _doAll; // Perform all of the device firmware updates bool _debugVerbose; // Display the most amount of data @@ -56,6 +57,7 @@ typedef struct _DEVICE_FIRMWARE_CTX size_t _fileBytes; // Length of the file in bytes bool _crcNeeded; // Does CRC need to be computed uint32_t _crc; // CRC value + size_t _crcBytes; // Number of bytes used to compute the CRC uint32_t _crcSave; // CRC computed during deviceFirmwareCrcReadData // Buffer support @@ -83,6 +85,13 @@ typedef struct _DEVICE_FIRMWARE_CTX size_t _validDataBytes; // Length of the valid data size_t _bytesRead; // Total number of bytes read + // CSV file + uint8_t * _csvFileData; // CSV file contents + size_t _csvFileBytes; // Size of the CSV file + int _csvLineCount; // Number of lines in the CSV file + int _csvFieldCount; // Number of fields per line in the CSV file + const char * _csvDeviceEntry; // Device entry in CSV file + // File objects for input or output File _nvmFile; // NVM file object SdFile _sdFile; // SD file object @@ -140,6 +149,9 @@ enum _DEVICE_FIRMWARE_UPDATE_STATE DFUS_DONE = 0, DFUS_INIT, DFUS_WAIT_NETWORK, + DFUS_CSV_OPEN, + DFUS_CSV_READ, + DFUS_CSV_CLOSE, DFUS_GET_DEVICE, DFUS_GET_NETWORK_FILES, DFUS_GET_HTTP_FILE_LIST_REQ, @@ -190,14 +202,20 @@ enum DFU_OUTPUT_DEVICE_TYPE //---------------------------------------- // Declare the generic device firmware update routines //---------------------------------------- -typedef void (* DEVICE_CLOSE)(DEVICE_FIRMWARE_CTX * ctx); -typedef bool (* DEVICE_OPEN)(DEVICE_FIRMWARE_CTX * ctx); -typedef bool (* DEVICE_RESET)(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec); -typedef ssize_t (* DEVICE_WRITE)(DEVICE_FIRMWARE_CTX * ctx, - const uint8_t * buffer, - size_t bytesToWrite); -typedef String (* GET_FIRMWARE_VERSION)(DEVICE_FIRMWARE_CTX * ctx); -typedef bool (* INIT_DEV_CTX)(DEVICE_FIRMWARE_CTX * ctx); +typedef void (* DFU_DEVICE_CLOSE)(DEVICE_FIRMWARE_CTX * ctx); +typedef bool (* DFU_DEVICE_OPEN)(DEVICE_FIRMWARE_CTX * ctx); +typedef bool (* DFU_DEVICE_RESET)(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec); +typedef ssize_t (* DFU_DEVICE_WRITE)(DEVICE_FIRMWARE_CTX * ctx, + const uint8_t * buffer, + size_t bytesToWrite); +typedef int (* DFU_GET_FIRMWARE_VERSION)(DEVICE_FIRMWARE_CTX * ctx); +typedef bool (* DFU_INIT_DEV_CTX)(DEVICE_FIRMWARE_CTX * ctx); +typedef int (* DFU_COMPARE_CSV_VERSION)(DEVICE_FIRMWARE_CTX * ctx, + int major, + int minor, + int patch, + int revision, + int releaseCandidate); //---------------------------------------- // Describe a device that needs firmware updates @@ -210,12 +228,13 @@ typedef struct _DEVICE_FIRMWARE_INFO const char * _directory; // Firmware directory const char * _nameData; // Data in file name, may be nullptr const char * _extension; // Data in file name (extension), may be nullptr - GET_FIRMWARE_VERSION _version; // Firmware version display routine - DEVICE_RESET _reset; // Reset the device before loading firmware - DEVICE_OPEN _open; // Prepare for firmware updates - DEVICE_WRITE _write; // Perform the firmware writes - DEVICE_CLOSE _close; // Perform firmware write cleanup - INIT_DEV_CTX _initDevCtx; // Initialize the device specific context + DFU_GET_FIRMWARE_VERSION _version; // Firmware version display routine + DFU_COMPARE_CSV_VERSION _cmpVersion; // Compare firmware versions + DFU_DEVICE_RESET _reset; // Reset the device before loading firmware + DFU_DEVICE_OPEN _open; // Prepare for firmware updates + DFU_DEVICE_WRITE _write; // Perform the firmware writes + DFU_DEVICE_CLOSE _close; // Perform firmware write cleanup + DFU_INIT_DEV_CTX _initDevCtx; // Initialize the device specific context size_t _devContextBytes; // Size of device specific context buffer bool _crcNeeded; // Is file CRC needed to do firmware update bool _useNvm; // Allow copy to NVM @@ -289,8 +308,22 @@ const int dfuBufferInfoCount = sizeof(dfuBufferInfo) / sizeof(dfuBufferInfo[0]); //---------------------------------------- // Get firmware version -String dfuEsp32GetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx); -String dfuGnssGetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx); +int dfuEsp32GetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx); +int dfuGnssGetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx); + +// Compare the CSV version to the current version +int dfuEsp32CompareCsvVersion(DEVICE_FIRMWARE_CTX * ctx, + int major, + int minor, + int patch, + int revision, + int releaseCandidate); +int dfuGnssCompareCsvVersion(DEVICE_FIRMWARE_CTX * ctx, + int major, + int minor, + int patch, + int revision, + int releaseCandidate); // Device reset bool dfuLg290pReset(DEVICE_FIRMWARE_CTX * ctx, uint32_t currentMsec); @@ -312,7 +345,8 @@ void dfuEsp32Close(DEVICE_FIRMWARE_CTX * ctx); void dfuLg290pClose(DEVICE_FIRMWARE_CTX * ctx); // Declare the begin routine -bool deviceFirmwareUpdateBegin(bool doAll, +bool deviceFirmwareUpdateBegin(const char * csvUrl, + bool doAll, bool debugVerbose, size_t saveDataLength = 8 * 1024); @@ -323,13 +357,13 @@ bool deviceFirmwareUpdateBegin(bool doAll, // Note: Use the JSON based OTA to get a new ESP32 image when the // parsing fails due to website changes on the servers below! const DEVICE_FIRMWARE_INFO deviceFirmwareInfo[] = -{// Name present Directory NameData Extension Firmware version Reset Open Write Close InitDevCtx Context Bytes CRC useNvm Buffer Bytes Max Write Bytes Server Branch dPrefix1 dPrefix2 dirEnd nPrefix nameEnd Raw Branch - {"ESP32", nullptr, nullptr, "Firmware_v", ".bin", dfuEsp32GetFirmwareVersion, nullptr, dfuEsp32Open, dfuEsp32Write, dfuEsp32Close, nullptr, 0, false, false, 0, 0, dfuGithub, nullptr, dfuTree, dfuItems, dfuListEnd, dfuName, dfuNameEnd, dfuRawHead}, +{// Name present Directory NameData Extension Firmware version Compare CSV version Reset Open Write Close InitDevCtx Context Bytes CRC useNvm Buffer Bytes Max Write Bytes Server Branch dPrefix1 dPrefix2 dirEnd nPrefix nameEnd Raw Branch + {"ESP32", nullptr, nullptr, "Firmware_v", ".bin", dfuEsp32GetFirmwareVersion, dfuEsp32CompareCsvVersion, nullptr, dfuEsp32Open, dfuEsp32Write, dfuEsp32Close, nullptr, 0, false, false, 0, 0, dfuGithub, nullptr, dfuTree, dfuItems, dfuListEnd, dfuName, dfuNameEnd, dfuRawHead}, // ESP32 must be the first entry in the list, p command does list in reverse // GNSS devices #ifdef COMPILE_LG290P - {"LG290P", &present.gnss_lg290p, "/gnss/lg290p", "LG290P", ".pkg", dfuGnssGetFirmwareVersion, dfuLg290pReset, dfuLg290pOpen, dfuLg290pWrite, dfuLg290pClose, nullptr, 0, true, false, DFU_LG290P_BYTES, DFU_LG290P_MAX_PAYLOAD_SIZE, dfuGithub, dfuRawHead, dfuFileTree, dfuItems, dfuListEnd, dfuName, dfuNameEnd, dfuRawHead}, + {"GNSS", &present.gnss_lg290p, "/gnss/lg290p", "LG290P", ".pkg", dfuGnssGetFirmwareVersion, dfuGnssCompareCsvVersion, dfuLg290pReset, dfuLg290pOpen, dfuLg290pWrite, dfuLg290pClose, nullptr, 0, true, false, DFU_LG290P_BYTES, DFU_LG290P_MAX_PAYLOAD_SIZE, dfuGithub, dfuRawHead, dfuFileTree, dfuItems, dfuListEnd, dfuName, dfuNameEnd, dfuRawHead}, #endif // COMPILE_LG290P }; const int deviceFirmwareInfoCount = sizeof(deviceFirmwareInfo) / sizeof(deviceFirmwareInfo[0]); diff --git a/Firmware/RTK_Everywhere/Device_Update_ESP32.ino b/Firmware/RTK_Everywhere/Device_Update_ESP32.ino index 932cae615..e2142aa11 100644 --- a/Firmware/RTK_Everywhere/Device_Update_ESP32.ino +++ b/Firmware/RTK_Everywhere/Device_Update_ESP32.ino @@ -63,14 +63,41 @@ void dfuEsp32Close(DEVICE_FIRMWARE_CTX * ctx) ctx->_complete = false; } +//---------------------------------------- +// Compare the CSV version to the current version +//---------------------------------------- +int dfuEsp32CompareCsvVersion(DEVICE_FIRMWARE_CTX * ctx, + int major, + int minor, + int patch, + int revision, + int releaseCandidate) +{ + // For debug builds, always return a negative value indicating to update + // to any release version + if ((FIRMWARE_VERSION_MAJOR == 99) && (FIRMWARE_VERSION_MINOR == 99)) + return -1; + + // Check if the current version is lower than the CSV version + if ((FIRMWARE_VERSION_MAJOR < major) + || ((FIRMWARE_VERSION_MAJOR == major) && (FIRMWARE_VERSION_MINOR < minor))) + return -1; + + // Check if the current version is higher than the CSV verison + if ((FIRMWARE_VERSION_MAJOR > major) + || ((FIRMWARE_VERSION_MAJOR == major) && (FIRMWARE_VERSION_MINOR > minor))) + return 1; + + // The version number match + return 0; +} + //---------------------------------------- // Get the current ESP32 firmware version //---------------------------------------- -String dfuEsp32GetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx) +int dfuEsp32GetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx) { - char version[128]; - //firmwareVersionGet(version, sizeof(version), true); - return String(version); + return RTK_IDENTIFIER; } //---------------------------------------- diff --git a/Firmware/RTK_Everywhere/Device_Update_GNSS.ino b/Firmware/RTK_Everywhere/Device_Update_GNSS.ino index 023c89ca9..73eb10899 100644 --- a/Firmware/RTK_Everywhere/Device_Update_GNSS.ino +++ b/Firmware/RTK_Everywhere/Device_Update_GNSS.ino @@ -4,10 +4,43 @@ Device_Update_GNSS.ino Support routines for GNSS devices =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +//---------------------------------------- +// Compare the CSV version to the current version +//---------------------------------------- +int dfuGnssCompareCsvVersion(DEVICE_FIRMWARE_CTX * ctx, + int major, + int minor, + int patch, + int revision, + int releaseCandidate) +{ + int gnssVersion = dfuGnssGetFirmwareVersion(ctx); + int gnssMajor = gnssVersion / 100; + int gnssMinor = gnssVersion % 100; + + // For debug builds, always return a negative value indicating to update + // to any release version + if ((gnssMajor == 99) && (gnssMinor == 99)) + return -1; + + // Check if the current version is lower than the CSV version + if ((gnssMajor < major) + || ((gnssMajor == major) && (gnssMinor < minor))) + return -1; + + // Check if the current version is higher than the CSV verison + if ((gnssMajor > major) + || ((gnssMajor == major) && (gnssMinor > minor))) + return 1; + + // The version number match + return 0; +} + //---------------------------------------- // Get the GNSS firmware version //---------------------------------------- -String dfuGnssGetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx) +int dfuGnssGetFirmwareVersion(DEVICE_FIRMWARE_CTX * ctx) { - return String(gnssFirmwareVersion); + return gnssFirmwareVersionInt; } diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index f2727cbd6..11236f296 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -499,6 +499,9 @@ char otaReportedVersion[50]; bool otaRequestFirmwareVersionCheck = false; bool otaRequestFirmwareUpdate = false; +//const char * csvUrl = "https://raw.githubusercontent.com/sparkfun/SparkFun_RTK_Everywhere_Firmware_Binaries/main/RTK-Everywhere-Variants.csv"; +const char * csvUrl = "https://leahyjr.com/SparkFun/RTK-Everywhere-Variants.csv"; + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // Connection settings to NTRIP Caster //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= @@ -1444,7 +1447,7 @@ void setup() DMW_b("imuFirmwareCheckUpdate"); if (imuCheckPassthroughFile() == true) // Check if updateImuFirmware.txt exists - imuBeginFirmwareUpdate(); // + imuBeginFirmwareUpdate(); // DMW_b("commandIndexFillActual"); commandIndexFillActual(); // Shrink the commandIndex table now we're certain what GNSS we have diff --git a/Firmware/RTK_Everywhere/menuFirmware.ino b/Firmware/RTK_Everywhere/menuFirmware.ino index 946f3f7ee..3824888f4 100644 --- a/Firmware/RTK_Everywhere/menuFirmware.ino +++ b/Firmware/RTK_Everywhere/menuFirmware.ino @@ -76,8 +76,10 @@ bool newOTAFirmwareAvailable = false; void firmwareMenu() { bool debugVerbose; + bool developerOptions; debugVerbose = false; + developerOptions = false; while (1) { systemPrintln(); @@ -92,11 +94,16 @@ void firmwareMenu() // fails in deviceFirmwareUpdate due to server website changes! // Letters: a c e i r s u otaMenuDisplay(currentVersion); - systemPrintf("d) Single device firmware update\r\n"); - systemPrintf("p) Program all firmware updates\r\n"); - systemPrintf("t) %s firmware debugging\r\n", settings.debugFirmwareUpdate ? "Disable" : "Enable"); - if (settings.debugFirmwareUpdate) - systemPrintf("v) %s verbose firmware debugging\r\n", debugVerbose ? "Disable" : "Enable"); + systemPrintf("D) %s developer options\r\n", developerOptions ? "Disable" : "Enable"); + systemPrintf("p) Restore product to production firmware\r\n"); + if (developerOptions) + { + systemPrintf("d) %s firmware debugging\r\n", settings.debugFirmwareUpdate ? "Disable" : "Enable"); + systemPrintf("l) Update all devices to latest released firmware\r\n"); + systemPrintf("o) Update one device's firmware\r\n"); + if (settings.debugFirmwareUpdate) + systemPrintf("v) %s verbose firmware debugging\r\n", debugVerbose ? "Disable" : "Enable"); + } for (int x = 0; x < binCount; x++) systemPrintf("%d) Load SD file: %s\r\n", x + 1, binFileNames[x]); @@ -119,26 +126,40 @@ void firmwareMenu() { } + // Enable / disable developer options + else if (incoming == 'D') + developerOptions ^= 1; + + // Toggle firmware debugging + else if (developerOptions && (incoming == 'd')) + { + settings.debugFirmwareUpdate ^= 1; + if (settings.debugFirmwareUpdate == false) + debugVerbose = false; + } + // Perform the device firmware update - else if ((incoming == 'd') || (incoming == 'p')) + else if (developerOptions && ((incoming == 'l') || (incoming == 'o'))) { - deviceFirmwareUpdateBegin(incoming == 'p', debugVerbose); + deviceFirmwareUpdateBegin(nullptr, incoming == 'l', debugVerbose); while (deviceFirmwareUpdate(millis())) { networkUpdate(); } } - // Toggle firmware debugging - else if (incoming == 't') + // Restore product to production firmware + else if (incoming == 'p') { - settings.debugFirmwareUpdate ^= 1; - if (settings.debugFirmwareUpdate == false) - debugVerbose = false; + deviceFirmwareUpdateBegin(csvUrl, false, debugVerbose); + while (deviceFirmwareUpdate(millis())) + { + networkUpdate(); + } } // Toggle verbose firmware debugging - else if (settings.debugFirmwareUpdate && (incoming == 'v')) + else if (developerOptions && settings.debugFirmwareUpdate && (incoming == 'v')) debugVerbose ^= 1; else if (incoming == 'x') diff --git a/Firmware/Tools/File_CRC.c b/Firmware/Tools/File_CRC.c new file mode 100644 index 000000000..4ddfbe307 --- /dev/null +++ b/Firmware/Tools/File_CRC.c @@ -0,0 +1,194 @@ +/********************************************************************** +* File_to_Hex_Data_Array.c +* +* Program to read a file and convert the contexts to C array of unsigned +* characters. +**********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +//---------------------------------------- +// Constants +//---------------------------------------- + +static const uint32_t crc32Table[256] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +//---------------------------------------- +// Globals +//---------------------------------------- + +int binFile; +uint8_t buffer[64 * 1024]; +size_t offset; +ssize_t validData; + +//---------------------------------------- +// Compute the CRC32 for the specified data region +//---------------------------------------- +uint32_t crc32Compute(uint32_t initialValue, const uint8_t * data, size_t length) +{ + uint32_t crc; + const uint8_t * end; + + // Compute the CRC for the data buffer + crc = initialValue ^ 0xffffffff; + end = &data[length]; + while (data < end) + crc = crc32Table[(crc ^ *data++) & 0xFF] ^ (crc >> 8); + crc ^= 0xffffffff; + return crc; +} + +//---------------------------------------- +// Read data from the file and compute the CRC +//---------------------------------------- +int readFileData(const char * fileName) +{ + ssize_t bytesRead; + uint32_t crc; + size_t crcBytes; + int exitStatus; + + exitStatus = 0; + crc = 0; + crcBytes = 0; + do + { + // Read some data from the file + bytesRead = read(binFile, buffer, sizeof(buffer)); + if (bytesRead < 0) + { + exitStatus = errno; + perror("ERROR: Failed to read from file!\n"); + break; + } + + // Compute the CRC on the bytes read + if (bytesRead) + { + // Compute the CRC + crc = crc32Compute(crc, buffer, bytesRead); + crcBytes += bytesRead; + } + } while (bytesRead); + + // Finish the array + if (exitStatus == 0) + { + printf("%s\r\n", fileName); + printf("Bytes, CRC: %ld,0x%08x\r\n", crcBytes, crc); + } + return exitStatus; +} + +//---------------------------------------- +// Application +//---------------------------------------- +int main(int argc, char ** argv) +{ + char * fileName; + int status; + + do + { + status = -1; + + // Display the help text + if (argc != 2) + { + printf ("%s filename\n", argv[0]); + return -1; + } + + // Open the file + fileName = argv[1]; + binFile = open(fileName, O_RDONLY); + if (binFile < 0) + { + status = errno; + perror("ERROR: Unable to open the file\n"); + } + + // Compute the CRC + status = readFileData(fileName); + } while (0); + + // Close the file + if (binFile >= 0) + close(binFile); + + return status; +} diff --git a/Firmware/Tools/makefile b/Firmware/Tools/makefile index 11115813d..e6337c359 100644 --- a/Firmware/Tools/makefile +++ b/Firmware/Tools/makefile @@ -17,6 +17,7 @@ EXECUTABLES += Read_Map_File EXECUTABLES += RTK_Reset EXECUTABLES += Split_Messages EXECUTABLES += X.509_crt_bundle_bin_to_c +EXECUTABLES += File_CRC EXECUTABLES += File_to_Hex_Data_Array INCLUDES = crc24q.h