diff --git a/core/.changelog.d/3035.fixed b/core/.changelog.d/3035.fixed new file mode 100644 index 000000000..fec90f308 --- /dev/null +++ b/core/.changelog.d/3035.fixed @@ -0,0 +1 @@ +[T2T1] Added a progress indicator for the formatting operation diff --git a/core/embed/extmod/modtrezorio/ff.c b/core/embed/extmod/modtrezorio/ff.c index b48b70b30..13586eaa4 100644 --- a/core/embed/extmod/modtrezorio/ff.c +++ b/core/embed/extmod/modtrezorio/ff.c @@ -418,6 +418,8 @@ #define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) +/* Helper macro for progress report */ +#define PROGRESS_SECTION(section_start, section_size, section_progress) (section_start + (section_progress * section_size)) /*-------------------------------------------------------------------------- @@ -3346,17 +3348,24 @@ FRESULT f_mkfs ( const TCHAR* path, /* Logical drive number */ const MKFS_PARM* opt, /* Format options */ void* work, /* Pointer to working buffer (null: use len bytes of heap memory) */ - UINT len /* Size of working buffer [byte] */ + UINT len, /* Size of working buffer [byte] */ + void (*progress_callback)(uint32_t current /* 0-1000 */) /* Callback used to report progress (NULL: no progress reporting) */ ) { static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */ + static const WORD progress_initial = 0; + static const WORD progress_before_initialize_fat_area = 10; + static const WORD progress_initialize_fat_area_section = 790; + static const WORD progress_before_initialize_root_directory = progress_before_initialize_fat_area + progress_initialize_fat_area_section; + static const WORD progress_initialize_root_directory_section = 100; + static const WORD progress_final = 1000; BYTE fsopt = 0, fsty = 0, sys = 0, pdrv = 0, ipart = 0; BYTE *buf = NULL; BYTE *pte = NULL; WORD ss = 0; /* Sector size */ - DWORD sz_buf = 0, sz_blk = 0, n_clst = 0, pau = 0, nsect = 0, n = 0, vsn = 0; + DWORD sz_buf = 0, sz_blk = 0, n_clst = 0, pau = 0, nsect = 0, nsect0 = 0, n = 0, vsn = 0; LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, Base LBA of volume, fat, data */ LBA_t sect = 0, lba[2] = {0}; DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, dir, data, cluster */ @@ -3365,6 +3374,9 @@ FRESULT f_mkfs ( DSTATUS ds = 0; FRESULT res = 0; + if (progress_callback) { + progress_callback(progress_initial); + } /* Check mounted drive and clear work area */ vol = get_ldnumber(&path); /* Get target logical drive */ @@ -3570,6 +3582,10 @@ FRESULT f_mkfs ( disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ } + if (progress_callback) { + progress_callback(progress_before_initialize_fat_area); + } + /* Initialize FAT area */ memset(buf, 0, sz_buf * ss); sect = b_fat; /* FAT start sector */ @@ -3581,24 +3597,40 @@ FRESULT f_mkfs ( } else { st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* FAT[0] and FAT[1] */ } - nsect = sz_fat; /* Number of FAT sectors */ + nsect0 = nsect = sz_fat; /* Number of FAT sectors */ do { /* Fill FAT sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); memset(buf, 0, ss); /* Rest of FAT all are cleared */ sect += n; nsect -= n; + + if (progress_callback) { + progress_callback(PROGRESS_SECTION(progress_before_initialize_fat_area, progress_initialize_fat_area_section, (1 - nsect / (float)nsect0))); + } } while (nsect); } + if (progress_callback) { + progress_callback(progress_before_initialize_root_directory); + } + /* Initialize root directory (fill with zero) */ - nsect = (fsty == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + nsect0 = nsect = (fsty == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ do { n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; + + if (progress_callback) { + progress_callback(PROGRESS_SECTION(progress_before_initialize_root_directory, progress_initialize_root_directory_section, (1 - nsect / (float)nsect0))); + } } while (nsect); } + if (progress_callback) { + progress_callback(progress_before_initialize_root_directory + progress_initialize_root_directory_section); + } + /* A FAT volume has been created here */ /* Determine system ID in the MBR partition table */ @@ -3632,6 +3664,10 @@ FRESULT f_mkfs ( if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + if (progress_callback) { + progress_callback(progress_final); + } + LEAVE_MKFS(FR_OK); } diff --git a/core/embed/extmod/modtrezorio/ff.h b/core/embed/extmod/modtrezorio/ff.h index 88fb31875..c26244be5 100644 --- a/core/embed/extmod/modtrezorio/ff.h +++ b/core/embed/extmod/modtrezorio/ff.h @@ -330,7 +330,7 @@ FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */ FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ -FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len, void (*progress_callback)(uint32_t current)); /* Create a FAT volume */ FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */ FRESULT f_setcp (WORD cp); /* Set current code page */ int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ diff --git a/core/embed/extmod/modtrezorio/modtrezorio-fatfs.h b/core/embed/extmod/modtrezorio/modtrezorio-fatfs.h index 1cf7f253f..6a7478ef5 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-fatfs.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-fatfs.h @@ -528,24 +528,42 @@ STATIC mp_obj_t mod_trezorio_fatfs_is_mounted() { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_fatfs_is_mounted_obj, mod_trezorio_fatfs_is_mounted); -/// def mkfs() -> None: +STATIC mp_obj_t ui_wait_callback = mp_const_none; + +STATIC void wrapped_ui_wait_callback(uint32_t current) { + if (mp_obj_is_callable(ui_wait_callback)) { + mp_call_function_1_protected(ui_wait_callback, mp_obj_new_int(current)); + } +} + +/// def mkfs(callback: Callable[[int], None] | None = None) -> None: /// """ /// Create a FAT volume on the SD card, /// """ -STATIC mp_obj_t mod_trezorio_fatfs_mkfs() { +STATIC mp_obj_t mod_trezorio_fatfs_mkfs(size_t n_args, const mp_obj_t *args) { if (_fatfs_instance_is_mounted()) { FATFS_RAISE(FatFSError, FR_LOCKED); } MKFS_PARM params = {FM_FAT32, 0, 0, 0, 0}; uint8_t working_buf[FF_MAX_SS] = {0}; - FRESULT res = f_mkfs("", ¶ms, working_buf, sizeof(working_buf)); + FRESULT res; + if (n_args == 1) { + // format with a progress callback + ui_wait_callback = args[0]; + res = f_mkfs("", ¶ms, working_buf, sizeof(working_buf), wrapped_ui_wait_callback); + ui_wait_callback = mp_const_none; + } else { + // format without a progress callback + res = f_mkfs("", ¶ms, working_buf, sizeof(working_buf), NULL); + } + if (res != FR_OK) { FATFS_RAISE(FatFSError, res); } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_fatfs_mkfs_obj, - mod_trezorio_fatfs_mkfs); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_fatfs_mkfs_obj, 0, + 1, mod_trezorio_fatfs_mkfs); /// def setlabel(label: str) -> None: /// """ diff --git a/core/mocks/generated/trezorio/fatfs.pyi b/core/mocks/generated/trezorio/fatfs.pyi index f845af409..9042d94b2 100644 --- a/core/mocks/generated/trezorio/fatfs.pyi +++ b/core/mocks/generated/trezorio/fatfs.pyi @@ -166,7 +166,7 @@ def is_mounted() -> bool: # extmod/modtrezorio/modtrezorio-fatfs.h -def mkfs() -> None: +def mkfs(callback: Callable[[int], None] | None = None) -> None: """ Create a FAT volume on the SD card, """ diff --git a/core/src/apps/common/sdcard.py b/core/src/apps/common/sdcard.py index e5d0ba96b..4537927d2 100644 --- a/core/src/apps/common/sdcard.py +++ b/core/src/apps/common/sdcard.py @@ -1,7 +1,12 @@ -from trezor import TR, io, wire +from typing import TYPE_CHECKING + +from trezor import TR, io, utils, wire from trezor.ui.layouts import confirm_action, show_error_and_raise from trezor.utils import sd_hotswap_enabled +if TYPE_CHECKING: + from trezor.ui.layouts.common import ProgressLayout + class SdCardUnavailable(wire.ProcessError): pass @@ -120,9 +125,15 @@ async def ensure_sdcard(ensure_filesystem: bool = True) -> None: # Proceed to formatting. Failure is caught by the outside OSError handler with sdcard.filesystem(mounted=False): - fatfs.mkfs() + render_func = None + if not utils.DISABLE_ANIMATION: + _start_progress() + render_func = _render_progress + fatfs.mkfs(render_func) fatfs.mount() fatfs.setlabel("TREZOR") + if not utils.DISABLE_ANIMATION: + _finish_progress() # format and mount succeeded return @@ -150,3 +161,29 @@ async def request_sd_salt() -> bytearray | None: # In either case, there is no good way to recover. If the user clicks Retry, # we will try again. await confirm_retry_sd() + + +_progress_obj: ProgressLayout | None = None + + +def _start_progress() -> None: + from trezor import workflow + from trezor.ui.layouts.progress import progress + + global _progress_obj + + # Because we are drawing to the screen manually, without a layout, we + # should make sure that no other layout is running. + workflow.close_others() + _progress_obj = progress() + + +def _render_progress(progress: int) -> None: + global _progress_obj + if _progress_obj is not None: + _progress_obj.report(progress) + + +def _finish_progress() -> None: + global _progress_obj + _progress_obj = None