mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Storage: New Virtual Mount API for FATFS disk images
This commit is contained in:
@@ -43,6 +43,7 @@ Storage* storage_app_alloc() {
|
||||
}
|
||||
|
||||
#ifndef FURI_RAM_EXEC
|
||||
storage_mnt_init(&app->storage[ST_MNT]);
|
||||
storage_int_init(&app->storage[ST_INT]);
|
||||
#endif
|
||||
storage_ext_init(&app->storage[ST_EXT]);
|
||||
|
||||
@@ -14,6 +14,7 @@ extern "C" {
|
||||
|
||||
#define STORAGE_INT_PATH_PREFIX "/int"
|
||||
#define STORAGE_EXT_PATH_PREFIX "/ext"
|
||||
#define STORAGE_MNT_PATH_PREFIX "/mnt"
|
||||
#define STORAGE_ANY_PATH_PREFIX "/any"
|
||||
#define STORAGE_APP_DATA_PATH_PREFIX "/data"
|
||||
#define STORAGE_APP_ASSETS_PATH_PREFIX "/assets"
|
||||
@@ -21,6 +22,7 @@ extern "C" {
|
||||
|
||||
#define INT_PATH(path) STORAGE_INT_PATH_PREFIX "/" path
|
||||
#define EXT_PATH(path) STORAGE_EXT_PATH_PREFIX "/" path
|
||||
#define MNT_PATH(path) STORAGE_MNT_PATH_PREFIX "/" path
|
||||
#define ANY_PATH(path) STORAGE_ANY_PATH_PREFIX "/" path
|
||||
#define APP_DATA_PATH(path) STORAGE_APP_DATA_PATH_PREFIX "/" path
|
||||
#define APP_ASSETS_PATH(path) STORAGE_APP_ASSETS_PATH_PREFIX "/" path
|
||||
@@ -555,6 +557,58 @@ FS_Error storage_int_backup(Storage* storage, const char* dstname);
|
||||
*/
|
||||
FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter);
|
||||
|
||||
/******************* FatFs Virtual Mount Functions *******************/
|
||||
|
||||
/**
|
||||
* @brief Initialize virual API with given disk image.
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @param image pointer to a File instance to base virtual mount on.
|
||||
* @return FSE_OK if the image was setup successfully.
|
||||
* @return FSE_ALREADY_OPEN if virtual API is already initialized.
|
||||
*/
|
||||
FS_Error storage_virtual_init(Storage* storage, File* image);
|
||||
|
||||
/**
|
||||
* @brief Format the virtual image.
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @return FSE_OK if the image was formatted successfully.
|
||||
* @return FSE_NOT_READY if virtual API is not initialized.
|
||||
* @return FSE_INTERNAL if an unknown error occurred.
|
||||
*/
|
||||
FS_Error storage_virtual_format(Storage* storage);
|
||||
|
||||
/**
|
||||
* @brief Mount the virtual image to /mnt.
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @return FSE_OK if the image was mounted successfully.
|
||||
* @return FSE_NOT_READY if virtual API is not initialized.
|
||||
* @return FSE_INVALID_PARAMETER if image has no supported filesystem.
|
||||
* @return FSE_INTERNAL if an unknown error occurred.
|
||||
*/
|
||||
FS_Error storage_virtual_mount(Storage* storage);
|
||||
|
||||
/**
|
||||
* @brief Unmount the virtual image from /mnt.
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @return FSE_OK if the image was unmounted successfully.
|
||||
* @return FSE_NOT_READY if virtual API is not initialized.
|
||||
* @return FSE_INTERNAL if an unknown error occurred.
|
||||
*/
|
||||
FS_Error storage_virtual_unmount(Storage* storage);
|
||||
|
||||
/**
|
||||
* @brief Quit virtual mount API, reset to allow new init.
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @return FSE_OK if the image was unloaded successfully.
|
||||
* @return FSE_NOT_READY if virtual API is not initialized.
|
||||
*/
|
||||
FS_Error storage_virtual_quit(Storage* storage);
|
||||
|
||||
/***************** Simplified Functions ******************/
|
||||
|
||||
/**
|
||||
|
||||
@@ -979,6 +979,49 @@ FS_Error storage_sd_status(Storage* storage) {
|
||||
return S_RETURN_ERROR;
|
||||
}
|
||||
|
||||
FS_Error storage_virtual_init(Storage* storage, File* image) {
|
||||
S_API_PROLOGUE;
|
||||
SAData data = {
|
||||
.virtualinit = {
|
||||
.image = image,
|
||||
}};
|
||||
S_API_MESSAGE(StorageCommandVirtualInit);
|
||||
S_API_EPILOGUE;
|
||||
return S_RETURN_ERROR;
|
||||
}
|
||||
|
||||
FS_Error storage_virtual_format(Storage* storage) {
|
||||
S_API_PROLOGUE;
|
||||
SAData data = {};
|
||||
S_API_MESSAGE(StorageCommandVirtualFormat);
|
||||
S_API_EPILOGUE;
|
||||
return S_RETURN_ERROR;
|
||||
}
|
||||
|
||||
FS_Error storage_virtual_mount(Storage* storage) {
|
||||
S_API_PROLOGUE;
|
||||
SAData data = {};
|
||||
S_API_MESSAGE(StorageCommandVirtualMount);
|
||||
S_API_EPILOGUE;
|
||||
return S_RETURN_ERROR;
|
||||
}
|
||||
|
||||
FS_Error storage_virtual_unmount(Storage* storage) {
|
||||
S_API_PROLOGUE;
|
||||
SAData data = {};
|
||||
S_API_MESSAGE(StorageCommandVirtualUnmount);
|
||||
S_API_EPILOGUE;
|
||||
return S_RETURN_ERROR;
|
||||
}
|
||||
|
||||
FS_Error storage_virtual_quit(Storage* storage) {
|
||||
S_API_PROLOGUE;
|
||||
SAData data = {};
|
||||
S_API_MESSAGE(StorageCommandVirtualQuit);
|
||||
S_API_EPILOGUE;
|
||||
return S_RETURN_ERROR;
|
||||
}
|
||||
|
||||
File* storage_file_alloc(Storage* storage) {
|
||||
File* file = malloc(sizeof(File));
|
||||
file->type = FileTypeClosed;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum { ST_EXT = 0, ST_INT = 1, ST_ANY, ST_ERROR } StorageType;
|
||||
typedef enum { ST_EXT = 0, ST_INT = 1, ST_MNT = 2, ST_ANY, ST_ERROR } StorageType;
|
||||
|
||||
typedef struct StorageData StorageData;
|
||||
|
||||
|
||||
@@ -104,6 +104,10 @@ typedef struct {
|
||||
SDInfo* info;
|
||||
} SAInfo;
|
||||
|
||||
typedef struct {
|
||||
File* image;
|
||||
} SAVirtualInit;
|
||||
|
||||
typedef union {
|
||||
SADataFOpen fopen;
|
||||
SADataFRead fread;
|
||||
@@ -127,6 +131,8 @@ typedef union {
|
||||
SADataRename rename;
|
||||
|
||||
SAInfo sdinfo;
|
||||
|
||||
SAVirtualInit virtualinit;
|
||||
} SAData;
|
||||
|
||||
typedef union {
|
||||
@@ -167,6 +173,11 @@ typedef enum {
|
||||
|
||||
StorageCommandFileExpand,
|
||||
StorageCommandCommonRename,
|
||||
StorageCommandVirtualInit,
|
||||
StorageCommandVirtualFormat,
|
||||
StorageCommandVirtualMount,
|
||||
StorageCommandVirtualUnmount,
|
||||
StorageCommandVirtualQuit,
|
||||
} StorageCommand;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -54,6 +54,8 @@ static StorageType storage_get_type_by_path(FuriString* path) {
|
||||
type = ST_EXT;
|
||||
} else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
|
||||
type = ST_INT;
|
||||
} else if(memcmp(path_cstr, STORAGE_MNT_PATH_PREFIX, strlen(STORAGE_MNT_PATH_PREFIX)) == 0) {
|
||||
type = ST_MNT;
|
||||
} else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
|
||||
type = ST_ANY;
|
||||
}
|
||||
@@ -787,6 +789,25 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
||||
case StorageCommandSDStatus:
|
||||
message->return_data->error_value = storage_process_sd_status(app);
|
||||
break;
|
||||
|
||||
// Virtual operations
|
||||
case StorageCommandVirtualInit:
|
||||
File* image = message->data->virtualinit.image;
|
||||
StorageData* image_storage = get_storage_by_file(image, app->storage);
|
||||
message->return_data->error_value = storage_process_virtual_init(image_storage, image);
|
||||
break;
|
||||
case StorageCommandVirtualFormat:
|
||||
message->return_data->error_value = storage_process_virtual_format(&app->storage[ST_MNT]);
|
||||
break;
|
||||
case StorageCommandVirtualMount:
|
||||
message->return_data->error_value = storage_process_virtual_mount(&app->storage[ST_MNT]);
|
||||
break;
|
||||
case StorageCommandVirtualUnmount:
|
||||
message->return_data->error_value = storage_process_virtual_unmount(&app->storage[ST_MNT]);
|
||||
break;
|
||||
case StorageCommandVirtualQuit:
|
||||
message->return_data->error_value = storage_process_virtual_quit(&app->storage[ST_MNT]);
|
||||
break;
|
||||
}
|
||||
|
||||
if(path != NULL) { //-V547
|
||||
|
||||
@@ -714,3 +714,181 @@ void storage_ext_init(StorageData* storage) {
|
||||
// do not notify on first launch, notifications app is waiting for our thread to read settings
|
||||
storage_ext_tick_internal(storage, false);
|
||||
}
|
||||
|
||||
#include "fatfs/ff_gen_drv.h"
|
||||
|
||||
#define SCSI_BLOCK_SIZE (0x200UL)
|
||||
static File* mnt_image = NULL;
|
||||
static StorageData* mnt_image_storage = NULL;
|
||||
bool mnt_mounted = false;
|
||||
;
|
||||
|
||||
FS_Error storage_process_virtual_init(StorageData* image_storage, File* image) {
|
||||
if(mnt_image) return FSE_ALREADY_OPEN;
|
||||
mnt_image = image;
|
||||
mnt_image_storage = image_storage;
|
||||
return FSE_OK;
|
||||
}
|
||||
|
||||
FS_Error storage_process_virtual_format(StorageData* storage) {
|
||||
#ifdef FURI_RAM_EXEC
|
||||
UNUSED(storage);
|
||||
return FSE_NOT_READY;
|
||||
#else
|
||||
if(!mnt_image) return FSE_NOT_READY;
|
||||
SDData* sd_data = storage->data;
|
||||
uint8_t* work = malloc(_MAX_SS);
|
||||
SDError error = f_mkfs(sd_data->path, FM_ANY, 0, work, _MAX_SS);
|
||||
free(work);
|
||||
if(error != FR_OK) return FSE_INTERNAL;
|
||||
return FSE_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
FS_Error storage_process_virtual_mount(StorageData* storage) {
|
||||
if(!mnt_image) return FSE_NOT_READY;
|
||||
SDData* sd_data = storage->data;
|
||||
SDError error = f_mount(sd_data->fs, sd_data->path, 1);
|
||||
if(error == FR_NO_FILESYSTEM) return FSE_INVALID_PARAMETER;
|
||||
if(error != FR_OK) return FSE_INTERNAL;
|
||||
mnt_mounted = true;
|
||||
return FSE_OK;
|
||||
}
|
||||
|
||||
FS_Error storage_process_virtual_unmount(StorageData* storage) {
|
||||
if(!mnt_image) return FSE_NOT_READY;
|
||||
SDData* sd_data = storage->data;
|
||||
SDError error = f_mount(0, sd_data->path, 0);
|
||||
if(error != FR_OK) return FSE_INTERNAL;
|
||||
mnt_mounted = false;
|
||||
return FSE_OK;
|
||||
}
|
||||
|
||||
FS_Error storage_process_virtual_quit(StorageData* storage) {
|
||||
if(!mnt_image) return FSE_NOT_READY;
|
||||
if(mnt_mounted) storage_process_virtual_unmount(storage);
|
||||
mnt_image = NULL;
|
||||
mnt_image_storage = NULL;
|
||||
return FSE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes a Drive
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
* @retval DSTATUS: Operation status
|
||||
*/
|
||||
static DSTATUS mnt_driver_initialize(BYTE pdrv) {
|
||||
UNUSED(pdrv);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets Disk Status
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
* @retval DSTATUS: Operation status
|
||||
*/
|
||||
static DSTATUS mnt_driver_status(BYTE pdrv) {
|
||||
UNUSED(pdrv);
|
||||
if(!mnt_image) return STA_NOINIT;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads Sector(s)
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
* @param *buff: Data buffer to store read data
|
||||
* @param sector: Sector address (LBA)
|
||||
* @param count: Number of sectors to read (1..128)
|
||||
* @retval DRESULT: Operation result
|
||||
*/
|
||||
static DRESULT mnt_driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
|
||||
UNUSED(pdrv);
|
||||
if(!storage_ext_file_seek(mnt_image_storage, mnt_image, sector * SCSI_BLOCK_SIZE, true)) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
size_t size = count * SCSI_BLOCK_SIZE;
|
||||
size_t read = storage_ext_file_read(mnt_image_storage, mnt_image, buff, size);
|
||||
return read == size ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes Sector(s)
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
* @param *buff: Data to be written
|
||||
* @param sector: Sector address (LBA)
|
||||
* @param count: Number of sectors to write (1..128)
|
||||
* @retval DRESULT: Operation result
|
||||
*/
|
||||
static DRESULT mnt_driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
|
||||
UNUSED(pdrv);
|
||||
if(!storage_ext_file_seek(mnt_image_storage, mnt_image, sector * SCSI_BLOCK_SIZE, true)) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
size_t size = count * SCSI_BLOCK_SIZE;
|
||||
size_t wrote = storage_ext_file_write(mnt_image_storage, mnt_image, buff, size);
|
||||
return wrote == size ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I/O control operation
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
* @param cmd: Control code
|
||||
* @param *buff: Buffer to send/receive control data
|
||||
* @retval DRESULT: Operation result
|
||||
*/
|
||||
static DRESULT mnt_driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
||||
DRESULT res = RES_ERROR;
|
||||
|
||||
DSTATUS status = mnt_driver_status(pdrv);
|
||||
if(status & STA_NOINIT) return RES_NOTRDY;
|
||||
|
||||
switch(cmd) {
|
||||
/* Make sure that no pending write process */
|
||||
case CTRL_SYNC:
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* Get number of sectors on the disk (DWORD) */
|
||||
case GET_SECTOR_COUNT:
|
||||
*(DWORD*)buff = storage_ext_file_size(mnt_image_storage, mnt_image) / SCSI_BLOCK_SIZE;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* Get R/W sector size (WORD) */
|
||||
case GET_SECTOR_SIZE:
|
||||
*(WORD*)buff = SCSI_BLOCK_SIZE;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* Get erase block size in unit of sector (DWORD) */
|
||||
case GET_BLOCK_SIZE:
|
||||
*(DWORD*)buff = SCSI_BLOCK_SIZE;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
default:
|
||||
res = RES_PARERR;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Diskio_drvTypeDef mnt_driver = {
|
||||
mnt_driver_initialize,
|
||||
mnt_driver_status,
|
||||
mnt_driver_read,
|
||||
mnt_driver_write,
|
||||
mnt_driver_ioctl,
|
||||
};
|
||||
|
||||
void storage_mnt_init(StorageData* storage) {
|
||||
char path[4] = {0};
|
||||
FATFS_LinkDriver(&mnt_driver, path);
|
||||
|
||||
SDData* sd_data = malloc(sizeof(SDData));
|
||||
sd_data->fs = malloc(sizeof(FATFS));
|
||||
sd_data->path = strdup(path);
|
||||
|
||||
storage->data = sd_data;
|
||||
storage->fs_api = &fs_api;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@ FS_Error sd_mount_card(StorageData* storage, bool notify);
|
||||
FS_Error sd_unmount_card(StorageData* storage);
|
||||
FS_Error sd_format_card(StorageData* storage);
|
||||
FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info);
|
||||
void storage_mnt_init(StorageData* storage);
|
||||
FS_Error storage_process_virtual_init(StorageData* image_storage, File* image);
|
||||
FS_Error storage_process_virtual_format(StorageData* storage);
|
||||
FS_Error storage_process_virtual_mount(StorageData* storage);
|
||||
FS_Error storage_process_virtual_unmount(StorageData* storage);
|
||||
FS_Error storage_process_virtual_quit(StorageData* storage);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3188,6 +3188,11 @@ Function,+,storage_sd_unmount,FS_Error,Storage*
|
||||
Function,+,storage_simply_mkdir,_Bool,"Storage*, const char*"
|
||||
Function,+,storage_simply_remove,_Bool,"Storage*, const char*"
|
||||
Function,+,storage_simply_remove_recursive,_Bool,"Storage*, const char*"
|
||||
Function,+,storage_virtual_format,FS_Error,Storage*
|
||||
Function,+,storage_virtual_init,FS_Error,"Storage*, File*"
|
||||
Function,+,storage_virtual_mount,FS_Error,Storage*
|
||||
Function,+,storage_virtual_quit,FS_Error,Storage*
|
||||
Function,+,storage_virtual_unmount,FS_Error,Storage*
|
||||
Function,-,stpcpy,char*,"char*, const char*"
|
||||
Function,-,stpncpy,char*,"char*, const char*, size_t"
|
||||
Function,+,strcasecmp,int,"const char*, const char*"
|
||||
|
||||
|
@@ -163,7 +163,7 @@
|
||||
/ Drive/Volume Configurations
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
#define _VOLUMES 2
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
/* USER CODE BEGIN Volumes */
|
||||
|
||||
Reference in New Issue
Block a user