Initial public release of the 2024A software.
This commit is contained in:
parent
7b9ad3edfd
commit
303e9e1dad
361 changed files with 60083 additions and 2 deletions
14
components/Reprogramming/CMakeLists.txt
Normal file
14
components/Reprogramming/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
idf_component_register(
|
||||
SRCS
|
||||
"Reprogramming.c"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
REQUIRES
|
||||
"SystemK"
|
||||
"driver"
|
||||
"usb"
|
||||
"usb_host_msc"
|
||||
"esp_https_ota"
|
||||
"app_update"
|
||||
"NVM"
|
||||
)
|
311
components/Reprogramming/Reprogramming.c
Normal file
311
components/Reprogramming/Reprogramming.c
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* This program source code file is part of the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* Copyright © 2024-2025 Joseph P. Kearney and the KTag developers.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* There should be a copy of the GNU Affero General Public License in the LICENSE
|
||||
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <SystemK.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <NVM.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
static const char *TAG = "Reprogramming";
|
||||
static const char *FIRMWARE_DIR = "/usb/esp/firmware";
|
||||
static const char *APP_IMAGE_PREFIX = "APP";
|
||||
static const char *SPIFFS_IMAGE_PREFIX = "SPIFFS";
|
||||
|
||||
#define REPROGRAMMING_TASK_PRIORITY 1
|
||||
static TaskHandle_t xReprogrammingTask = NULL;
|
||||
static StaticTask_t xReprogrammingTaskBuffer;
|
||||
static StackType_t xReprogrammingTaskStack[4096];
|
||||
|
||||
#define BUFFER_SIZE 1024
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long total_size;
|
||||
unsigned long remaining_size;
|
||||
void *data;
|
||||
} data_manager_t;
|
||||
|
||||
typedef enum {
|
||||
IMAGE_TYPE_APP,
|
||||
IMAGE_TYPE_SPIFFS
|
||||
} image_type_t;
|
||||
|
||||
static char *find_image_filename(const char *prefix)
|
||||
{
|
||||
DIR *dir = opendir(FIRMWARE_DIR);
|
||||
if (dir == NULL)
|
||||
{
|
||||
KLOG_ERROR(TAG, "Failed to open directory '%s': %s!", FIRMWARE_DIR, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
char *filename = NULL;
|
||||
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if (entry->d_type == DT_REG && strncmp(entry->d_name, prefix, strlen(prefix)) == 0)
|
||||
{
|
||||
filename = strdup(entry->d_name);
|
||||
if (filename == NULL)
|
||||
{
|
||||
KLOG_ERROR(TAG, "Memory allocation failed!");
|
||||
}
|
||||
else
|
||||
{
|
||||
KLOG_DEBUG(TAG, "Found image: %s", filename);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
if (filename == NULL)
|
||||
{
|
||||
KLOG_WARN(TAG, "Couldn't find an image with prefix '%s' in directory '%s'!", prefix, FIRMWARE_DIR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t needed = snprintf(NULL, 0, "%s/%s", FIRMWARE_DIR, filename) + 1;
|
||||
char *full_path = malloc(needed);
|
||||
if (full_path != NULL)
|
||||
{
|
||||
snprintf(full_path, needed, "%s/%s", FIRMWARE_DIR, filename);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
return full_path;
|
||||
}
|
||||
|
||||
static SystemKResult_T program_image(const char *filename, image_type_t image_type)
|
||||
{
|
||||
esp_err_t err;
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (file == NULL)
|
||||
{
|
||||
KLOG_ERROR(TAG, "Failed to open file %s", filename);
|
||||
return SYSTEMK_RESULT_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long file_size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
KLOG_INFO(TAG, "File size: %ld bytes", file_size);
|
||||
|
||||
data_manager_t data_manager = {
|
||||
.total_size = file_size,
|
||||
.remaining_size = file_size,
|
||||
.data = malloc(BUFFER_SIZE)
|
||||
};
|
||||
|
||||
if (data_manager.data == NULL)
|
||||
{
|
||||
KLOG_ERROR(TAG, "Failed to allocate memory");
|
||||
fclose(file);
|
||||
return SYSTEMK_RESULT_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
if (image_type == IMAGE_TYPE_APP)
|
||||
{
|
||||
esp_ota_handle_t update_handle = 0;
|
||||
const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
KLOG_INFO(TAG, "Writing to partition subtype %d at offset 0x%" PRIx32, update_partition->subtype, update_partition->address);
|
||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
KLOG_ERROR(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
|
||||
free(data_manager.data);
|
||||
fclose(file);
|
||||
return SYSTEMK_RESULT_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
while (data_manager.remaining_size > 0)
|
||||
{
|
||||
size_t size = (data_manager.remaining_size <= BUFFER_SIZE) ? data_manager.remaining_size : BUFFER_SIZE;
|
||||
if (fread(data_manager.data, 1, size, file) != size)
|
||||
{
|
||||
KLOG_ERROR(TAG, "fread failed");
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
err = esp_ota_write(update_handle, data_manager.data, size);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
KLOG_ERROR(TAG, "esp_ota_write failed (%s)", esp_err_to_name(err));
|
||||
break;
|
||||
}
|
||||
|
||||
data_manager.remaining_size -= size;
|
||||
}
|
||||
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
err = esp_ota_end(update_handle);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
KLOG_ERROR(TAG, "esp_ota_end failed (%s)", esp_err_to_name(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
KLOG_ERROR(TAG, "esp_ota_set_boot_partition failed (%s)", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (image_type == IMAGE_TYPE_SPIFFS)
|
||||
{
|
||||
const esp_partition_t *spiffs_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
|
||||
if (spiffs_partition == NULL)
|
||||
{
|
||||
KLOG_ERROR(TAG, "SPIFFS partition not found");
|
||||
free(data_manager.data);
|
||||
fclose(file);
|
||||
return SYSTEMK_RESULT_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
err = esp_partition_erase_range(spiffs_partition, 0, spiffs_partition->size);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
KLOG_ERROR(TAG, "Failed to erase SPIFFS partition (%s)", esp_err_to_name(err));
|
||||
free(data_manager.data);
|
||||
fclose(file);
|
||||
return SYSTEMK_RESULT_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
while (data_manager.remaining_size > 0)
|
||||
{
|
||||
size_t size = (data_manager.remaining_size <= BUFFER_SIZE) ? data_manager.remaining_size : BUFFER_SIZE;
|
||||
if (fread(data_manager.data, 1, size, file) != size)
|
||||
{
|
||||
KLOG_ERROR(TAG, "fread failed");
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
err = esp_partition_write(spiffs_partition, spiffs_partition->size - data_manager.remaining_size, data_manager.data, size);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
KLOG_ERROR(TAG, "esp_partition_write failed (%s)", esp_err_to_name(err));
|
||||
break;
|
||||
}
|
||||
|
||||
data_manager.remaining_size -= size;
|
||||
}
|
||||
}
|
||||
|
||||
free(data_manager.data);
|
||||
fclose(file);
|
||||
|
||||
return (err == ESP_OK) ? SYSTEMK_RESULT_SUCCESS : SYSTEMK_RESULT_UNSPECIFIED_FAILURE;
|
||||
}
|
||||
|
||||
SystemKResult_T Maybe_Reprogram_From_USB(void)
|
||||
{
|
||||
SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;
|
||||
char *app_image_filename = find_image_filename(APP_IMAGE_PREFIX);
|
||||
char *spiffs_image_filename = find_image_filename(SPIFFS_IMAGE_PREFIX);
|
||||
|
||||
if (app_image_filename)
|
||||
{
|
||||
KLOG_INFO(TAG, "Application image found: %s", app_image_filename);
|
||||
result = program_image(app_image_filename, IMAGE_TYPE_APP);
|
||||
free(app_image_filename);
|
||||
if (result != SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (spiffs_image_filename)
|
||||
{
|
||||
KLOG_INFO(TAG, "SPIFFS image found: %s", spiffs_image_filename);
|
||||
result = program_image(spiffs_image_filename, IMAGE_TYPE_SPIFFS);
|
||||
free(spiffs_image_filename);
|
||||
}
|
||||
|
||||
if ((app_image_filename == NULL) && (spiffs_image_filename == NULL))
|
||||
{
|
||||
KLOG_ERROR(TAG, "Reprogramming failed--there's nothing to reprogram!");
|
||||
result = SYSTEMK_RESULT_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Reprogramming_Task(void *pvParameters)
|
||||
{
|
||||
SystemKResult_T result = Maybe_Reprogram_From_USB();
|
||||
static All_On_Data_T data;
|
||||
|
||||
if (result == SYSTEMK_RESULT_SUCCESS)
|
||||
{
|
||||
KLOG_INFO(TAG, "Reprogramming succeeded--rebooting...");
|
||||
data.color = ApplyMask(COLOR_GREEN, 0x70);
|
||||
data.style = DISPLAY_STYLE_SOLID;
|
||||
}
|
||||
else
|
||||
{
|
||||
KLOG_ERROR(TAG, "Reprogramming failed--rebooting...");
|
||||
data.color = ApplyMask(COLOR_RED, 0x70);
|
||||
data.style = DISPLAY_STYLE_SOLID;
|
||||
}
|
||||
|
||||
NeoPixelsAction_T neopixels_action = {.ID = NEOPIXELS_ALL_ON, .Prominence = NEOPIXELS_FOREGROUND, .Data = (void *)&data};
|
||||
xQueueSend(xQueueNeoPixels, &neopixels_action, 0);
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void Request_Reprogramming_From_USB(void)
|
||||
{
|
||||
if (xReprogrammingTask == NULL)
|
||||
{
|
||||
KLOG_INFO(TAG, "Attempting USB reprogramming...");
|
||||
|
||||
xReprogrammingTask = xTaskCreateStaticPinnedToCore(
|
||||
Reprogramming_Task,
|
||||
"Reprogramming",
|
||||
sizeof(xReprogrammingTaskStack) / sizeof(StackType_t),
|
||||
NULL,
|
||||
REPROGRAMMING_TASK_PRIORITY,
|
||||
xReprogrammingTaskStack,
|
||||
&xReprogrammingTaskBuffer,
|
||||
APP_CPU_NUM);
|
||||
}
|
||||
else
|
||||
{
|
||||
KLOG_WARN(TAG, "Reprogramming already in progress.");
|
||||
}
|
||||
}
|
22
components/Reprogramming/Reprogramming.h
Normal file
22
components/Reprogramming/Reprogramming.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* This program source code file is part of the KTag project.
|
||||
*
|
||||
* 🛡️ <https://ktag.clubk.club> 🃞
|
||||
*
|
||||
* Copyright © 2024-2025 Joseph P. Kearney and the KTag developers.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* There should be a copy of the GNU Affero General Public License in the LICENSE
|
||||
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
void Request_Reprogramming_From_USB(void);
|
Loading…
Add table
Add a link
Reference in a new issue