Skip to content

Commit df04619

Browse files
committed
Updater: First working prototype
1 parent 7fe7381 commit df04619

File tree

3 files changed

+140
-63
lines changed

3 files changed

+140
-63
lines changed

launcher/main/main.c

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -144,22 +144,47 @@ static rg_gui_event_t startup_app_cb(rg_gui_option_t *option, rg_gui_event_t eve
144144
return RG_DIALOG_VOID;
145145
}
146146

147-
#ifdef RG_ENABLE_NETWORKING
148147
static rg_gui_event_t updater_cb(rg_gui_option_t *option, rg_gui_event_t event)
149148
{
150-
if (rg_network_get_info().state != RG_NETWORK_CONNECTED)
149+
if (event == RG_DIALOG_ENTER)
151150
{
152-
option->flags = RG_DIALOG_FLAG_DISABLED;
153-
return RG_DIALOG_VOID;
151+
const rg_gui_option_t options[] = {
152+
#if defined(RG_ENABLE_NETWORKING) && RG_UPDATER_ENABLE
153+
{1, _("Check for updates"), NULL, RG_DIALOG_FLAG_NORMAL, NULL},
154+
#endif
155+
#if defined(RG_UPDATER_APPLICATION)
156+
{2, _("Reboot to installer"), NULL, RG_DIALOG_FLAG_NORMAL, NULL},
157+
#endif
158+
RG_DIALOG_END,
159+
};
160+
int sel = rg_gui_dialog(_("Update Retro-Go"), options, 0);
161+
#if defined(RG_ENABLE_NETWORKING) && RG_UPDATER_ENABLE
162+
if (sel == 1)
163+
updater_show_dialog();
164+
#endif
165+
#if defined(RG_UPDATER_APPLICATION)
166+
if (sel == 2)
167+
rg_system_switch_app(RG_UPDATER_APPLICATION, NULL, NULL, 0, 0);
168+
#endif
169+
return RG_DIALOG_REDRAW;
154170
}
171+
return RG_DIALOG_VOID;
172+
}
173+
174+
static rg_gui_event_t prebuild_cache_cb(rg_gui_option_t *option, rg_gui_event_t event)
175+
{
155176
if (event == RG_DIALOG_ENTER)
156177
{
157-
updater_show_dialog();
158-
return RG_DIALOG_REDRAW;
178+
rg_input_wait_for_key(RG_KEY_ANY, false, 1000);
179+
#ifdef RG_ENABLE_NETWORKING
180+
webui_stop();
181+
#endif
182+
crc_cache_prebuild();
159183
}
160184
return RG_DIALOG_VOID;
161185
}
162186

187+
#ifdef RG_ENABLE_NETWORKING
163188
static rg_gui_event_t webui_switch_cb(rg_gui_option_t *option, rg_gui_event_t event)
164189
{
165190
bool enabled = rg_settings_get_number(NS_APP, SETTING_WEBUI, 0);
@@ -176,19 +201,6 @@ static rg_gui_event_t webui_switch_cb(rg_gui_option_t *option, rg_gui_event_t ev
176201
}
177202
#endif
178203

179-
static rg_gui_event_t prebuild_cache_cb(rg_gui_option_t *option, rg_gui_event_t event)
180-
{
181-
if (event == RG_DIALOG_ENTER)
182-
{
183-
rg_input_wait_for_key(RG_KEY_ANY, false, 1000);
184-
#ifdef RG_ENABLE_NETWORKING
185-
webui_stop();
186-
#endif
187-
crc_cache_prebuild();
188-
}
189-
return RG_DIALOG_VOID;
190-
}
191-
192204
static void retro_loop(void)
193205
{
194206
tab_t *tab = NULL;
@@ -432,9 +444,7 @@ static void options_handler(rg_gui_option_t *dest)
432444
static void about_handler(rg_gui_option_t *dest)
433445
{
434446
*dest++ = (rg_gui_option_t){0, _("Build CRC cache"), NULL, RG_DIALOG_FLAG_NORMAL, &prebuild_cache_cb};
435-
#if defined(RG_ENABLE_NETWORKING) && RG_UPDATER_ENABLE
436-
*dest++ = (rg_gui_option_t){0, _("Check for updates"), NULL, RG_DIALOG_FLAG_NORMAL, &updater_cb};
437-
#endif
447+
*dest++ = (rg_gui_option_t){0, _("Update Retro-Go"), NULL, RG_DIALOG_FLAG_NORMAL, &updater_cb};
438448
*dest++ = (rg_gui_option_t)RG_DIALOG_END;
439449
}
440450

rg_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
PROJECT_ICON = os.getenv("PROJECT_ICON", "assets/icon.raw")
2020
PROJECT_APPS = {
2121
# Project name Type, SubType, Size
22-
'updater': [0, 0, 524288],
2322
'launcher': [0, 0, 1048576],
23+
'updater': [0, 0, 524288], # Should be first to allow growth but it interfere with boot for now
2424
'retro-core': [0, 0, 1048576],
2525
'prboom-go': [0, 0, 851968],
2626
'gwenesis': [0, 0, 1048576],

updater/main/main.c

Lines changed: 107 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <esp_flash_partitions.h>
1010
#include <esp_partition.h>
1111
#include <esp_spi_flash.h>
12+
#include <esp_ota_ops.h>
1213

1314
#ifdef RG_UPDATER_DOWNLOAD_LOCATION
1415
#define DOWNLOAD_LOCATION RG_UPDATER_DOWNLOAD_LOCATION
@@ -33,37 +34,36 @@ typedef struct
3334
char reserved[180];
3435
} img_info_t;
3536

36-
#define CONFIRM(title, message...) \
37-
{ \
38-
snprintf(message_buffer, sizeof(message_buffer), message); \
39-
if (!rg_gui_confirm(_(title), message_buffer, false)) \
40-
return false; \
41-
rg_display_clear(C_BLACK); \
37+
#define FORMAT(message...) ({snprintf(message_buffer, sizeof(message_buffer), message); message_buffer; })
38+
#define CONFIRM(title, message...) \
39+
{ \
40+
if (!rg_gui_confirm(_(title), FORMAT(message), false)) \
41+
return false; \
42+
rg_display_clear(C_BLACK); \
4243
}
43-
#define ALERT(title, message...) \
44-
{ \
45-
snprintf(message_buffer, sizeof(message_buffer), message); \
46-
rg_gui_alert(_(title), message_buffer); \
47-
rg_display_clear(C_BLACK); \
44+
#define TRY(cond, error_message) \
45+
if (!(cond)) \
46+
{ \
47+
rg_gui_alert(_("Error"), error_message); \
48+
return false; \
4849
}
49-
#define ERROR(message...) \
50-
{ \
51-
ALERT("Error", message); \
52-
return false; \
53-
}
54-
#define TRY(cond, error_message) \
55-
if (!(cond)) \
56-
ERROR(error_message);
50+
51+
typedef struct
52+
{
53+
const esp_partition_info_t *src;
54+
const esp_partition_t *dst;
55+
} partition_pair_t;
5756

5857
static rg_app_t *app;
59-
static esp_partition_info_t device_partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES];
60-
static char message_buffer[256];
58+
// static esp_partition_info_t device_partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES];
6159

6260
static bool do_update(const char *filename)
6361
{
62+
char message_buffer[256];
6463
esp_partition_info_t partition_table[16]; // ESP_PARTITION_TABLE_MAX_ENTRIES
6564
int num_partitions = 0;
6665
img_info_t img_info;
66+
void *buffer;
6767
int filesize = 0;
6868
FILE *fp;
6969

@@ -101,16 +101,86 @@ static bool do_update(const char *filename)
101101
// TODO: Also support images that truncate the first 0x1000, just in case
102102
TRY(fseek(fp, ESP_PARTITION_TABLE_OFFSET, SEEK_SET) == 0, "File seek failed");
103103
TRY(fread(&partition_table, sizeof(partition_table), 1, fp), "File read failed");
104+
TRY(esp_partition_table_verify(partition_table, true, &num_partitions) == ESP_OK, "File is not a valid ESP32 image.\nCannot continue.");
105+
106+
const char *current_partition = esp_ota_get_running_partition()->label;
107+
partition_pair_t queue[num_partitions];
108+
size_t queue_count = 0;
109+
110+
// At this time we only flash partitions of type app and subtype ota_X
111+
for (int i = 0; i < num_partitions; ++i)
112+
{
113+
const esp_partition_info_t *src = &partition_table[i];
114+
const esp_partition_t *dst = esp_partition_find_first(src->type, ESP_PARTITION_SUBTYPE_ANY, (char *)src->label);
115+
116+
if (src->type != PART_TYPE_APP || (src->subtype & 0xF0) != PART_SUBTYPE_OTA_FLAG)
117+
RG_LOGW("Skipping partition %.15s: Unsupported type.", (char *)src->label);
118+
else if (!dst)
119+
RG_LOGW("Skipping partition %.15s: No match found.", (char *)src->label);
120+
else if ((dst->subtype & 0xF0) != PART_SUBTYPE_OTA_FLAG)
121+
RG_LOGW("Skipping partition %.15s: Not an OTA partition.", (char *)src->label);
122+
else if (strncmp(current_partition, dst->label, 16) == 0)
123+
RG_LOGW("Skipping partition %.15s: Currently running.", (char *)src->label);
124+
else if (dst->size < src->pos.size)
125+
RG_LOGW("Skipping partition %.15s: New partition is bigger.", (char *)src->label);
126+
else
127+
{
128+
queue[queue_count].src = src;
129+
queue[queue_count].dst = dst;
130+
queue_count++;
131+
}
132+
}
133+
134+
TRY(queue_count > 0, "Found no updatable partition!");
104135

105-
if (esp_partition_table_verify(partition_table, true, &num_partitions) != ESP_OK)
106-
ERROR("File is not a valid esp32 image.\nCannot continue.");
136+
int pos = 0;
137+
for (size_t i = 0; i < queue_count; ++i)
138+
pos += snprintf(message_buffer + pos, sizeof(message_buffer) - pos, "- %s\n", queue[i].dst->label);
139+
pos += snprintf(message_buffer + pos, sizeof(message_buffer) - pos, "\nProceed?");
107140

108-
ALERT("Info", "Image contains %d partitions.", num_partitions);
141+
if (!rg_gui_confirm("Partitions to be updated:", message_buffer, false))
142+
return false;
109143

110-
// We can work around this, this is for debugging purposes
111-
if (memcmp(&device_partition_table, &partition_table, sizeof(partition_table)))
112-
ALERT("Error", "Partition table mismatch.");
144+
TRY(buffer = malloc(2 * 1024 * 1024), "Memory allocation failed");
113145

146+
// TODO: Implement scrolling. Maybe on rg_gui side?
147+
// Or at least support continue writing on the same line. "... Complete"
148+
int y_pos = 9999;
149+
#define SCREEN_PRINTF(color, message...) \
150+
if (y_pos + 16 > rg_display_get_height()) \
151+
rg_display_clear(C_BLACK), y_pos = 0; \
152+
y_pos += rg_gui_draw_text(0, y_pos, 0, FORMAT(message), color, C_BLACK, 0).height;
153+
154+
SCREEN_PRINTF(C_WHITE, "Starting...");
155+
156+
for (size_t i = 0; i < queue_count; ++i)
157+
{
158+
const esp_partition_info_t *src = queue[i].src;
159+
const esp_partition_t *dst = queue[i].dst;
160+
esp_ota_handle_t ota_handle;
161+
esp_err_t ret;
162+
if ((ret = esp_ota_begin(queue[i].dst, 0, &ota_handle)) != ESP_OK)
163+
{
164+
SCREEN_PRINTF(C_RED, "Skipping %s: %s", dst->label, esp_err_to_name(ret));
165+
continue;
166+
}
167+
SCREEN_PRINTF(C_WHITE, "Reading %s from file...", dst->label);
168+
if (fseek(fp, src->pos.offset, SEEK_SET) != 0 || !fread(buffer, src->pos.size, 1, fp))
169+
{
170+
SCREEN_PRINTF(C_RED, "File read error");
171+
continue;
172+
}
173+
SCREEN_PRINTF(C_WHITE, "Writing %s to flash...", dst->label);
174+
if ((ret = esp_ota_write(ota_handle, buffer, src->pos.size)) == ESP_OK) {
175+
SCREEN_PRINTF(C_GREEN, "Complete!");
176+
} else {
177+
SCREEN_PRINTF(C_RED, "Failed: %s", esp_err_to_name(ret));
178+
}
179+
esp_ota_end(ota_handle);
180+
}
181+
free(buffer);
182+
183+
rg_gui_alert(_("All done"), "The process is complete");
114184
return true;
115185
}
116186

@@ -123,28 +193,25 @@ void app_main(void)
123193

124194
if (!rg_storage_ready())
125195
{
126-
ALERT("Error", "Storage mount failed.\nMake sure the card is FAT32.");
196+
rg_gui_alert(_("Error"), "Storage mount failed.\nMake sure the card is FAT32.");
127197
rg_system_exit();
128198
}
129199

130-
if (spi_flash_read(ESP_PARTITION_TABLE_OFFSET, &device_partition_table, sizeof(device_partition_table)) != ESP_OK)
131-
{
132-
ALERT("Error", "Failed to read device's partition table!");
133-
rg_system_exit();
134-
}
200+
// if (spi_flash_read(ESP_PARTITION_TABLE_OFFSET, &device_partition_table, sizeof(device_partition_table)) != ESP_OK)
201+
// {
202+
// rg_gui_alert(_("Error"), "Failed to read device's partition table!");
203+
// rg_system_exit();
204+
// }
135205

136-
const char *filename = app->romPath;
206+
// const char *filename = app->romPath;
137207
while (true)
138208
{
209+
char *filename = rg_gui_file_picker("Select update", DOWNLOAD_LOCATION, NULL, true, true);
139210
if (!filename || !*filename)
140-
{
141-
filename = rg_gui_file_picker("Select update", DOWNLOAD_LOCATION, NULL, true, true);
142-
if (!filename || !*filename)
143-
break;
144-
}
211+
break;
145212
if (do_update(filename))
146213
break;
147-
filename = NULL;
214+
free(filename);
148215
}
149216

150217
rg_system_exit();

0 commit comments

Comments
 (0)