Added USB debug commands handling + file_get_sectors refactor

This commit is contained in:
Mateusz Faderewski 2023-09-04 21:09:31 +02:00
parent c44ff7f752
commit f063f05fe6
10 changed files with 243 additions and 47 deletions

View File

@ -42,6 +42,7 @@ SRCS = \
menu/rom_database.c \ menu/rom_database.c \
menu/settings.c \ menu/settings.c \
menu/sound.c \ menu/sound.c \
menu/usb_comm.c \
menu/views/browser.c \ menu/views/browser.c \
menu/views/credits.c \ menu/views/credits.c \
menu/views/error.c \ menu/views/error.c \

View File

@ -21,8 +21,14 @@ echo !!! Now toggle power to the N64 !!!
echo: echo:
echo: echo:
if not "%1" == "/d" goto :exit if not "%1" == "/d" goto :not_d
%~dp0tools\sc64\sc64deployer debug --no-writeback %~dp0tools\sc64\sc64deployer debug --no-writeback
:exit :not_d
if not "%1" == "/du" goto :not_du
%~dp0tools\sc64\sc64deployer debug --no-writeback --init "send-file /sc64menu.n64 @output/sc64menu.n64@;reboot"
:not_du

View File

@ -4,28 +4,12 @@ set -e
REMOTE="--remote ${REMOTE:-host.docker.internal:9064}" REMOTE="--remote ${REMOTE:-host.docker.internal:9064}"
## FIXME: this does not work!
# Make sure we are connected
#echo Detecting SC64...
#sc64deployer $REMOTE list
# Get the information
echo SC64 info...:
sc64deployer $REMOTE info
echo
echo
# Load the ROM
echo Loading ROM...:
sc64deployer $REMOTE upload ./output/N64FlashcartMenu.n64 sc64deployer $REMOTE upload ./output/N64FlashcartMenu.n64
echo
echo
# Toggle the power of the N64 to boot the ROM.
echo !!! Now toggle power to the N64 !!!
echo
echo
if [ "$1" = "-d" ]; then if [ "$1" = "-d" ]; then
sc64deployer $REMOTE debug --no-writeback sc64deployer $REMOTE debug --no-writeback
fi fi
if [ "$1" = "-du" ]; then
sc64deployer $REMOTE debug --no-writeback --init "send-file /sc64menu.n64 @output/sc64menu.n64@;reboot"
fi

View File

@ -11,7 +11,7 @@
#include "sc64/sc64.h" #include "sc64/sc64.h"
#define WRITEBACK_MAX_SECTORS (256) #define SAVE_WRITEBACK_MAX_SECTORS (256)
static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
@ -24,6 +24,9 @@ static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
KiB(128), KiB(128),
}; };
static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
static flashcart_error_t dummy_init (void) { static flashcart_error_t dummy_init (void) {
return FLASHCART_OK; return FLASHCART_OK;
} }
@ -95,6 +98,7 @@ flashcart_error_t flashcart_deinit (void) {
if (flashcart->deinit) { if (flashcart->deinit) {
return flashcart->deinit(); return flashcart->deinit();
} }
return FLASHCART_OK; return FLASHCART_OK;
} }
@ -124,9 +128,21 @@ flashcart_error_t flashcart_load_file (char *file_path, uint32_t rom_offset, uin
return flashcart->load_file(file_path, rom_offset, file_offset); return flashcart->load_file(file_path, rom_offset, file_offset);
} }
static void save_writeback_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) {
for (uint32_t i = 0; i < cluster_size; i++) {
uint32_t offset = file_sector + i;
uint32_t sector = cluster_sector + i;
if ((offset > SAVE_WRITEBACK_MAX_SECTORS) || (offset > sector_count)) {
return;
}
save_writeback_sectors[offset] = sector;
}
}
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) { flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) {
flashcart_error_t error; flashcart_error_t error;
uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
if (save_type >= __FLASHCART_SAVE_TYPE_END) { if (save_type >= __FLASHCART_SAVE_TYPE_END) {
return FLASHCART_ERROR_ARGS; return FLASHCART_ERROR_ARGS;
@ -158,10 +174,13 @@ flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t sa
} }
if (flashcart->set_save_writeback) { if (flashcart->set_save_writeback) {
if (file_get_sectors(save_path, sectors, WRITEBACK_MAX_SECTORS)) { for (int i = 0; i < SAVE_WRITEBACK_MAX_SECTORS; i++) {
save_writeback_sectors[i] = 0;
}
if (file_get_sectors(save_path, save_writeback_sectors_callback)) {
return FLASHCART_ERROR_LOAD; return FLASHCART_ERROR_LOAD;
} }
if ((error = flashcart->set_save_writeback(sectors)) != FLASHCART_OK) { if ((error = flashcart->set_save_writeback(save_writeback_sectors)) != FLASHCART_OK) {
return error; return error;
} }
} }

View File

@ -85,7 +85,7 @@ static flashcart_error_t sc64_init (void) {
uint32_t value; uint32_t value;
} default_config[] = { } default_config[] = {
{ CFG_ID_BOOTLOADER_SWITCH, false }, { CFG_ID_BOOTLOADER_SWITCH, false },
{ CFG_ID_ROM_WRITE_ENABLE, false }, { CFG_ID_ROM_WRITE_ENABLE, true },
{ CFG_ID_ROM_SHADOW_ENABLE, false }, { CFG_ID_ROM_SHADOW_ENABLE, false },
{ CFG_ID_DD_MODE, DD_MODE_DISABLED }, { CFG_ID_DD_MODE, DD_MODE_DISABLED },
{ CFG_ID_ISV_ADDRESS, 0x00000000 }, { CFG_ID_ISV_ADDRESS, 0x00000000 },
@ -110,6 +110,8 @@ static flashcart_error_t sc64_init (void) {
} }
static flashcart_error_t sc64_deinit (void) { static flashcart_error_t sc64_deinit (void) {
sc64_ll_set_config(CFG_ID_ROM_WRITE_ENABLE, false);
sc64_ll_lock(); sc64_ll_lock();
return FLASHCART_OK; return FLASHCART_OK;

View File

@ -14,6 +14,7 @@
#include "png_decoder.h" #include "png_decoder.h"
#include "settings.h" #include "settings.h"
#include "sound.h" #include "sound.h"
#include "usb_comm.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "views/views.h" #include "views/views.h"
@ -180,6 +181,8 @@ void menu_run (boot_params_t *boot_params) {
sound_poll(); sound_poll();
png_decoder_poll(); png_decoder_poll();
usb_comm_poll(menu);
} }
menu_deinit(menu); menu_deinit(menu);

163
src/menu/usb_comm.c Normal file
View File

@ -0,0 +1,163 @@
// NOTE: This code doesn't implement EverDrive-64 USB protocol.
// Main use of these functions is to aid menu development
// (for example replace files on the SD card or reboot menu).
#include <fatfs/ff.h>
#include <string.h>
#include <usb.h>
#include "usb_comm.h"
#include "utils/utils.h"
#define MAX_FILE_SIZE MiB(4)
typedef struct {
const char *id;
void (*op) (menu_t *menu);
} usb_comm_command_t;
static int usb_comm_get_char (void) {
char c;
if (USBHEADER_GETSIZE(usb_poll()) <= 0) {
return -1;
}
usb_read(&c, sizeof(c));
return (int) (c);
}
static bool usb_comm_read_string (char *string, int length, char end) {
for (int i = 0; i < length; i++) {
int c = usb_comm_get_char();
if (c < 0) {
return true;
}
string[i] = (char) (c);
if (c == '\0' || c == end) {
string[i] = '\0';
break;
}
if (i == (length - 1)) {
return true;
}
}
return false;
}
static void usb_comm_send_error (const char *message) {
usb_purge();
usb_write(DATATYPE_TEXT, message, strlen(message));
}
static void command_reboot (menu_t *menu) {
menu->next_mode = MENU_MODE_BOOT;
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH;
menu->boot_params->detect_cic_seed = true;
};
static void command_send_file (menu_t *menu) {
char path[256];
char length[8];
FIL f;
int remaining;
uint8_t data[8192];
UINT bytes_written;
if (usb_comm_read_string(path, sizeof(path), ' ')) {
return usb_comm_send_error("Invalid path argument\n");
}
if (usb_comm_get_char() != '@') {
return usb_comm_send_error("Invalid argument\n");
}
if (usb_comm_read_string(length, sizeof(length), '@')) {
return usb_comm_send_error("Invalid file length argument\n");
}
if (f_open(&f, path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) {
return usb_comm_send_error("Couldn't create file\n");
}
remaining = atoi(length);
if (remaining > MAX_FILE_SIZE) {
return usb_comm_send_error("File size too big\n");
}
while (remaining > 0) {
int block_size = MIN(remaining, sizeof(data));
usb_read(data, block_size);
if (f_write(&f, data, block_size, &bytes_written) != FR_OK) {
f_close(&f);
return usb_comm_send_error("Couldn't write data to the file\n");
}
if (bytes_written != block_size) {
f_close(&f);
return usb_comm_send_error("Couldn't write all required data to the file\n");
}
remaining -= block_size;
}
if (f_close(&f) != FR_OK) {
return usb_comm_send_error("Couldn't flush data to the file\n");
}
if (usb_comm_get_char() != '\0') {
return usb_comm_send_error("Invalid token at the end of data stream\n");
}
}
static usb_comm_command_t commands[] = {
{ .id = "reboot", .op = command_reboot },
{ .id = "send-file", .op = command_send_file },
{ .id = NULL },
};
void usb_comm_poll (menu_t *menu) {
uint32_t header = usb_poll();
if (USBHEADER_GETTYPE(header) != DATATYPE_TEXT) {
usb_purge();
return;
}
if (USBHEADER_GETSIZE(header) > 0) {
char cmd_id[32];
if (usb_comm_read_string(cmd_id, sizeof(cmd_id), ' ')) {
usb_comm_send_error("Command id too long\n");
} else {
usb_comm_command_t *cmd = commands;
while (cmd->id != NULL) {
if (strcmp(cmd->id, cmd_id) == 0) {
cmd->op(menu);
break;
}
cmd++;
}
usb_purge();
if (cmd->id == NULL) {
usb_comm_send_error("Unknown command\n");
}
}
}
}

21
src/menu/usb_comm.h Normal file
View File

@ -0,0 +1,21 @@
/**
* @file usb_comm.h
* @brief USB communication subsystem
* @ingroup menu
*/
#ifndef USB_COMM_H__
#define USB_COMM_H__
#include "menu_state.h"
#ifndef NDEBUG
void usb_comm_poll (menu_t *menu);
#else
#define usb_comm_poll(menu)
#endif
#endif

View File

@ -13,7 +13,7 @@ char *strip_sd_prefix (char *path) {
char *found = strstr(path, prefix); char *found = strstr(path, prefix);
if (found) { if (found) {
return found + strlen(prefix); return found + strlen(prefix) - 1;
} }
return path; return path;
@ -106,13 +106,13 @@ bool file_fill (char *path, uint8_t value) {
return error; return error;
} }
bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) { bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size)) {
FATFS *fs; FATFS *fs;
FIL fil; FIL fil;
bool error = false; bool error = false;
for (int i = 0; i < entries; i++) { if (!callback) {
sectors[i] = 0; return true;
} }
if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) { if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) {
@ -121,25 +121,22 @@ bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
fs = fil.obj.fs; fs = fil.obj.fs;
uint32_t file_sector_entries = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE); uint32_t sector_count = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE);
file_sector_entries = (file_sector_entries > entries) ? entries : file_sector_entries;
uint32_t cluster_sector = 0; uint32_t cluster_sector = 0;
for (int file_sector = 0; file_sector < file_sector_entries; file_sector++) { for (int file_sector = 0; file_sector < sector_count; file_sector += fs->csize) {
if ((file_sector % fs->csize) == 0) { if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) {
if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { error = true;
error = true; break;
break;
}
uint32_t cluster = fil.clust;
if (cluster >= fs->n_fatent) {
error = true;
break;
}
cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2)));
} }
*sectors++ = (cluster_sector + (file_sector % fs->csize)); uint32_t cluster = fil.clust;
if (cluster >= fs->n_fatent) {
error = true;
break;
}
cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2)));
callback(sector_count, file_sector, cluster_sector, fs->csize);
} }
if (f_close(&fil) != FR_OK) { if (f_close(&fil) != FR_OK) {

View File

@ -17,7 +17,7 @@ size_t file_get_size (char *path);
bool file_delete (char *path); bool file_delete (char *path);
bool file_allocate (char *path, size_t size); bool file_allocate (char *path, size_t size);
bool file_fill (char *path, uint8_t value); bool file_fill (char *path, uint8_t value);
bool file_get_sectors (char *path, uint32_t *sectors, size_t entries); bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size));
bool file_has_extensions (char *path, const char *extensions[]); bool file_has_extensions (char *path, const char *extensions[]);
bool directory_exists (char *path); bool directory_exists (char *path);