mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-12 06:35:22 -05:00
Rewrite dircache generation to take advantage for the FAT code. Reduce RAM usage by ~30Kb and binsize by at least several hundreds bytes. Also remove the directory depth limit of dircache.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24657 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
e1d8c3ef7c
commit
26c0e753d1
2 changed files with 186 additions and 201 deletions
|
|
@ -71,7 +71,6 @@ static unsigned long entry_count = 0;
|
||||||
static unsigned long reserve_used = 0;
|
static unsigned long reserve_used = 0;
|
||||||
static unsigned int cache_build_ticks = 0;
|
static unsigned int cache_build_ticks = 0;
|
||||||
static unsigned long appflags = 0;
|
static unsigned long appflags = 0;
|
||||||
static char dircache_cur_path[MAX_PATH];
|
|
||||||
|
|
||||||
static struct event_queue dircache_queue;
|
static struct event_queue dircache_queue;
|
||||||
static long dircache_stack[(DEFAULT_STACK_SIZE + 0x900)/sizeof(long)];
|
static long dircache_stack[(DEFAULT_STACK_SIZE + 0x900)/sizeof(long)];
|
||||||
|
|
@ -147,11 +146,6 @@ static struct dircache_entry* dircache_gen_down(struct dircache_entry *ce)
|
||||||
return next_entry;
|
return next_entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This will eat ~30 KiB of memory!
|
|
||||||
* We should probably use that as additional reserve buffer in future. */
|
|
||||||
#define MAX_SCAN_DEPTH 16
|
|
||||||
static struct travel_data dir_recursion[MAX_SCAN_DEPTH];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there is an event waiting in the queue
|
* Returns true if there is an event waiting in the queue
|
||||||
* that requires the current operation to be aborted.
|
* that requires the current operation to be aborted.
|
||||||
|
|
@ -176,130 +170,111 @@ static bool check_event_queue(void)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#ifndef SIMULATOR
|
||||||
* Internal function to iterate a path.
|
/* scan and build static data (avoid redundancy on stack) */
|
||||||
*/
|
static struct
|
||||||
static int dircache_scan(IF_MV2(int volume,) struct travel_data *td)
|
|
||||||
{
|
{
|
||||||
#ifdef SIMULATOR
|
|
||||||
#ifdef HAVE_MULTIVOLUME
|
#ifdef HAVE_MULTIVOLUME
|
||||||
(void)volume;
|
int volume;
|
||||||
#endif
|
|
||||||
while ( ( td->entry = readdir_uncached(td->dir) ) )
|
|
||||||
#else
|
|
||||||
while ( (fat_getnext(td->dir, &td->entry) >= 0) && (td->entry.name[0]))
|
|
||||||
#endif
|
#endif
|
||||||
|
struct fat_dir *dir;
|
||||||
|
struct fat_direntry *direntry;
|
||||||
|
}sab;
|
||||||
|
|
||||||
|
static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce)
|
||||||
|
{
|
||||||
|
/* normally, opendir expects a full fat_dir as parent but in our case,
|
||||||
|
* it's completely useless because we don't modify anything
|
||||||
|
* WARNING: this heavily relies on current FAT implementation ! */
|
||||||
|
|
||||||
|
/* those field are necessary to update the FAT entry in case of modification
|
||||||
|
here we don't touch anything so we put dummy values */
|
||||||
|
sab.dir->entry = 0;
|
||||||
|
sab.dir->entrycount = 0;
|
||||||
|
sab.dir->file.firstcluster = 0;
|
||||||
|
/* open directory */
|
||||||
|
int rc = fat_opendir(IF_MV2(sab.volume,) sab.dir, startcluster, sab.dir);
|
||||||
|
if(rc < 0)
|
||||||
{
|
{
|
||||||
#ifdef SIMULATOR
|
logf("fat_opendir failed: %d", rc);
|
||||||
if (!strcmp(".", td->entry->d_name) ||
|
return rc;
|
||||||
!strcmp("..", td->entry->d_name))
|
}
|
||||||
{
|
|
||||||
|
/* first pass : read dir */
|
||||||
|
struct dircache_entry *first_ce = ce;
|
||||||
|
|
||||||
|
/* read through directory */
|
||||||
|
while((rc = fat_getnext(sab.dir, sab.direntry)) >= 0 && sab.direntry->name[0])
|
||||||
|
{
|
||||||
|
if(!strcmp(".", sab.direntry->name) ||
|
||||||
|
!strcmp("..", sab.direntry->name))
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
td->ce->attribute = td->entry->attribute;
|
ce->attribute = sab.direntry->attr;
|
||||||
td->ce->name_len = strlen(td->entry->d_name) + 1;
|
ce->name_len = strlen(sab.direntry->name) + 1;
|
||||||
td->ce->d_name = ((char *)dircache_root+dircache_size);
|
ce->d_name = ((char *)dircache_root + dircache_size);
|
||||||
td->ce->size = td->entry->size;
|
ce->startcluster = sab.direntry->firstcluster;
|
||||||
td->ce->wrtdate = td->entry->wrtdate;
|
ce->size = sab.direntry->filesize;
|
||||||
td->ce->wrttime = td->entry->wrttime;
|
ce->wrtdate = sab.direntry->wrtdate;
|
||||||
memcpy(td->ce->d_name, td->entry->d_name, td->ce->name_len);
|
ce->wrttime = sab.direntry->wrttime;
|
||||||
#else
|
memcpy(ce->d_name, sab.direntry->name, ce->name_len);
|
||||||
if (!strcmp(".", td->entry.name) ||
|
|
||||||
!strcmp("..", td->entry.name))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
td->ce->attribute = td->entry.attr;
|
dircache_size += ce->name_len;
|
||||||
td->ce->name_len = strlen(td->entry.name) + 1;
|
|
||||||
td->ce->d_name = ((char *)dircache_root+dircache_size);
|
|
||||||
td->ce->startcluster = td->entry.firstcluster;
|
|
||||||
td->ce->size = td->entry.filesize;
|
|
||||||
td->ce->wrtdate = td->entry.wrtdate;
|
|
||||||
td->ce->wrttime = td->entry.wrttime;
|
|
||||||
memcpy(td->ce->d_name, td->entry.name, td->ce->name_len);
|
|
||||||
#endif
|
|
||||||
dircache_size += td->ce->name_len;
|
|
||||||
entry_count++;
|
entry_count++;
|
||||||
|
|
||||||
#ifdef SIMULATOR
|
if(ce->attribute & FAT_ATTR_DIRECTORY)
|
||||||
if (td->entry->attribute & ATTR_DIRECTORY)
|
dircache_gen_down(ce);
|
||||||
#else
|
|
||||||
if (td->entry.attr & FAT_ATTR_DIRECTORY)
|
ce = dircache_gen_next(ce);
|
||||||
#endif
|
if(ce == NULL)
|
||||||
{
|
|
||||||
|
|
||||||
td->down_entry = dircache_gen_down(td->ce);
|
|
||||||
if (td->down_entry == NULL)
|
|
||||||
return -2;
|
|
||||||
|
|
||||||
td->pathpos = strlen(dircache_cur_path);
|
|
||||||
strlcpy(&dircache_cur_path[td->pathpos], "/",
|
|
||||||
sizeof(dircache_cur_path) - td->pathpos);
|
|
||||||
#ifdef SIMULATOR
|
|
||||||
strlcpy(&dircache_cur_path[td->pathpos+1], td->entry->d_name,
|
|
||||||
sizeof(dircache_cur_path) - td->pathpos - 1);
|
|
||||||
|
|
||||||
td->newdir = opendir_uncached(dircache_cur_path);
|
|
||||||
if (td->newdir == NULL)
|
|
||||||
{
|
|
||||||
logf("Failed to opendir_uncached(): %s", dircache_cur_path);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
strlcpy(&dircache_cur_path[td->pathpos+1], td->entry.name,
|
|
||||||
sizeof(dircache_cur_path) - td->pathpos - 1);
|
|
||||||
|
|
||||||
td->newdir = *td->dir;
|
|
||||||
if (fat_opendir(IF_MV2(volume,) &td->newdir,
|
|
||||||
td->entry.firstcluster, td->dir) < 0 )
|
|
||||||
{
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
td->ce = dircache_gen_next(td->ce);
|
|
||||||
if (td->ce == NULL)
|
|
||||||
return -4;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
td->ce->down = NULL;
|
|
||||||
td->ce = dircache_gen_next(td->ce);
|
|
||||||
if (td->ce == NULL)
|
|
||||||
return -5;
|
return -5;
|
||||||
|
|
||||||
/* When simulator is used, it's only safe to yield here. */
|
/* When simulator is used, it's only safe to yield here. */
|
||||||
if (thread_enabled)
|
if(thread_enabled)
|
||||||
{
|
{
|
||||||
/* Stop if we got an external signal. */
|
/* Stop if we got an external signal. */
|
||||||
if (check_event_queue())
|
if(check_event_queue())
|
||||||
return -6;
|
return -6;
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
/* add "." and ".." */
|
||||||
|
ce->d_name = ".";
|
||||||
|
ce->name_len = 2;
|
||||||
|
ce->attribute = FAT_ATTR_DIRECTORY;
|
||||||
|
ce->startcluster = startcluster;
|
||||||
|
ce->size = 0;
|
||||||
|
ce->down = first_ce;
|
||||||
|
|
||||||
|
ce = dircache_gen_next(ce);
|
||||||
|
|
||||||
|
ce->d_name = "..";
|
||||||
|
ce->name_len = 3;
|
||||||
|
ce->attribute = FAT_ATTR_DIRECTORY;
|
||||||
|
ce->startcluster = first_ce->up->startcluster;
|
||||||
|
ce->size = 0;
|
||||||
|
ce->down = first_ce->up;
|
||||||
|
|
||||||
|
/* second pass: recurse ! */
|
||||||
|
ce = first_ce;
|
||||||
|
|
||||||
|
while(ce)
|
||||||
|
{
|
||||||
|
if(ce->name_len != 0 && ce->down != NULL && strcmp(ce->d_name, ".") && strcmp(ce->d_name, ".."))
|
||||||
|
sab_process_dir(ce->startcluster, ce->down);
|
||||||
|
|
||||||
|
ce = ce->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int dircache_scan_and_build(IF_MV2(int volume,) struct dircache_entry *ce)
|
||||||
* Recursively scan the hard disk and build the cache.
|
|
||||||
*/
|
|
||||||
#ifdef SIMULATOR
|
|
||||||
static int dircache_travel(IF_MV2(int volume,) DIR_UNCACHED *dir, struct dircache_entry *ce)
|
|
||||||
#else
|
|
||||||
static int dircache_travel(IF_MV2(int volume,) struct fat_dir *dir, struct dircache_entry *ce)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
int depth = 0;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
memset(ce, 0, sizeof(struct dircache_entry));
|
memset(ce, 0, sizeof(struct dircache_entry));
|
||||||
|
|
||||||
#if defined(HAVE_MULTIVOLUME) && !defined(SIMULATOR)
|
#ifdef HAVE_MULTIVOLUME
|
||||||
if (volume > 0)
|
if (volume > 0)
|
||||||
{
|
{
|
||||||
ce->d_name = ((char *)dircache_root+dircache_size);
|
ce->d_name = ((char *)dircache_root+dircache_size);
|
||||||
|
|
@ -313,81 +288,117 @@ static int dircache_travel(IF_MV2(int volume,) struct fat_dir *dir, struct dirca
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dir_recursion[0].dir = dir;
|
struct fat_dir dir; /* allocate on stack once for all scan */
|
||||||
dir_recursion[0].ce = ce;
|
struct fat_direntry direntry; /* ditto */
|
||||||
dir_recursion[0].first = ce;
|
#ifdef HAVE_MULTIVOLUME
|
||||||
|
sab.volume = volume;
|
||||||
do {
|
|
||||||
//logf("=> %s", dircache_cur_path);
|
|
||||||
result = dircache_scan(IF_MV2(volume,) &dir_recursion[depth]);
|
|
||||||
switch (result) {
|
|
||||||
case 0: /* Leaving the current directory. */
|
|
||||||
/* Add the standard . and .. entries. */
|
|
||||||
ce = dir_recursion[depth].ce;
|
|
||||||
ce->d_name = ".";
|
|
||||||
ce->name_len = 2;
|
|
||||||
#ifdef SIMULATOR
|
|
||||||
closedir_uncached(dir_recursion[depth].dir);
|
|
||||||
ce->attribute = ATTR_DIRECTORY;
|
|
||||||
#else
|
|
||||||
ce->attribute = FAT_ATTR_DIRECTORY;
|
|
||||||
ce->startcluster = dir_recursion[depth].dir->file.firstcluster;
|
|
||||||
#endif
|
#endif
|
||||||
ce->size = 0;
|
sab.dir = &dir;
|
||||||
ce->down = dir_recursion[depth].first;
|
sab.direntry = &direntry;
|
||||||
|
|
||||||
depth--;
|
return sab_process_dir(0, ce);
|
||||||
if (depth < 0)
|
}
|
||||||
break ;
|
#else /* !SIMULATOR */
|
||||||
|
static char sab_path[MAX_PATH];
|
||||||
|
|
||||||
dircache_cur_path[dir_recursion[depth].pathpos] = '\0';
|
static int sab_process_dir(struct dircache_entry *ce)
|
||||||
|
{
|
||||||
ce = dircache_gen_next(ce);
|
struct dirent_uncached *entry;
|
||||||
if (ce == NULL)
|
struct dircache_entry *first_ce = ce;
|
||||||
{
|
DIR_UNCACHED *dir = opendir_uncached(sab_path);
|
||||||
logf("memory allocation error");
|
if(dir == NULL)
|
||||||
return -3;
|
{
|
||||||
}
|
logf("Failed to opendir_uncached(%s)", sab_path);
|
||||||
#ifdef SIMULATOR
|
return -1;
|
||||||
ce->attribute = ATTR_DIRECTORY;
|
}
|
||||||
#else
|
|
||||||
ce->attribute = FAT_ATTR_DIRECTORY;
|
|
||||||
ce->startcluster = dir_recursion[depth].dir->file.firstcluster;
|
|
||||||
#endif
|
|
||||||
ce->d_name = "..";
|
|
||||||
ce->name_len = 3;
|
|
||||||
ce->size = 0;
|
|
||||||
ce->down = dir_recursion[depth].first;
|
|
||||||
|
|
||||||
break ;
|
while((entry = readdir_uncached(dir)))
|
||||||
|
{
|
||||||
case 1: /* Going down in the directory tree. */
|
if(!strcmp(".", entry->d_name) ||
|
||||||
depth++;
|
!strcmp("..", entry->d_name))
|
||||||
if (depth >= MAX_SCAN_DEPTH)
|
continue;
|
||||||
{
|
|
||||||
logf("Too deep directory structure");
|
ce->attribute = entry->attribute;
|
||||||
return -2;
|
ce->name_len = strlen(entry->d_name) + 1;
|
||||||
}
|
ce->d_name = ((char *)dircache_root + dircache_size);
|
||||||
|
ce->size = entry->size;
|
||||||
#ifdef SIMULATOR
|
ce->wrtdate = entry->wrtdate;
|
||||||
dir_recursion[depth].dir = dir_recursion[depth-1].newdir;
|
ce->wrttime = entry->wrttime;
|
||||||
#else
|
memcpy(ce->d_name, entry->d_name, ce->name_len);
|
||||||
dir_recursion[depth].dir = &dir_recursion[depth-1].newdir;
|
|
||||||
#endif
|
dircache_size += ce->name_len;
|
||||||
dir_recursion[depth].first = dir_recursion[depth-1].down_entry;
|
entry_count++;
|
||||||
dir_recursion[depth].ce = dir_recursion[depth-1].down_entry;
|
|
||||||
break ;
|
if(entry->attribute & ATTR_DIRECTORY)
|
||||||
|
{
|
||||||
default:
|
dircache_gen_down(ce);
|
||||||
logf("Scan failed");
|
if(ce->down == NULL)
|
||||||
logf("-> %s", dircache_cur_path);
|
{
|
||||||
|
closedir_uncached(dir);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
/* save current paths size */
|
||||||
|
int pathpos = strlen(sab_path);
|
||||||
|
/* append entry */
|
||||||
|
strlcpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos);
|
||||||
|
strlcpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1);
|
||||||
|
|
||||||
|
int rc = sab_process_dir(ce->down);
|
||||||
|
/* restore path */
|
||||||
|
sab_path[pathpos] = '\0';
|
||||||
|
|
||||||
|
if(rc < 0)
|
||||||
|
{
|
||||||
|
closedir_uncached(dir);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (depth >= 0) ;
|
|
||||||
|
ce = dircache_gen_next(ce);
|
||||||
|
if(ce == NULL)
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
/* When simulator is used, it's only safe to yield here. */
|
||||||
|
if(thread_enabled)
|
||||||
|
{
|
||||||
|
/* Stop if we got an external signal. */
|
||||||
|
if(check_event_queue())
|
||||||
|
return -1;
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add "." and ".." */
|
||||||
|
ce->d_name = ".";
|
||||||
|
ce->name_len = 2;
|
||||||
|
ce->attribute = ATTR_DIRECTORY;
|
||||||
|
ce->size = 0;
|
||||||
|
ce->down = first_ce;
|
||||||
|
|
||||||
|
ce = dircache_gen_next(ce);
|
||||||
|
|
||||||
|
ce->d_name = "..";
|
||||||
|
ce->name_len = 3;
|
||||||
|
ce->attribute = ATTR_DIRECTORY;
|
||||||
|
ce->size = 0;
|
||||||
|
ce->down = first_ce->up;
|
||||||
|
|
||||||
|
closedir_uncached(dir);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dircache_scan_and_build(IF_MV2(int volume,) struct dircache_entry *ce)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_MULTIVOLUME
|
||||||
|
(void) volume;
|
||||||
|
#endif
|
||||||
|
memset(ce, 0, sizeof(struct dircache_entry));
|
||||||
|
|
||||||
|
strlcpy(sab_path, "/", sizeof sab_path);
|
||||||
|
return sab_process_dir(ce);
|
||||||
|
}
|
||||||
|
#endif /* SIMULATOR */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal function to get a pointer to dircache_entry for a given filename.
|
* Internal function to get a pointer to dircache_entry for a given filename.
|
||||||
* path: Absolute path to a file or directory (see comment)
|
* path: Absolute path to a file or directory (see comment)
|
||||||
|
|
@ -578,11 +589,6 @@ int dircache_save(void)
|
||||||
*/
|
*/
|
||||||
static int dircache_do_rebuild(void)
|
static int dircache_do_rebuild(void)
|
||||||
{
|
{
|
||||||
#ifdef SIMULATOR
|
|
||||||
DIR_UNCACHED *pdir;
|
|
||||||
#else
|
|
||||||
struct fat_dir dir, *pdir;
|
|
||||||
#endif
|
|
||||||
unsigned int start_tick;
|
unsigned int start_tick;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
@ -592,7 +598,6 @@ static int dircache_do_rebuild(void)
|
||||||
appflags = 0;
|
appflags = 0;
|
||||||
entry_count = 0;
|
entry_count = 0;
|
||||||
|
|
||||||
memset(dircache_cur_path, 0, sizeof(dircache_cur_path));
|
|
||||||
dircache_size = sizeof(struct dircache_entry);
|
dircache_size = sizeof(struct dircache_entry);
|
||||||
|
|
||||||
#ifdef HAVE_MULTIVOLUME
|
#ifdef HAVE_MULTIVOLUME
|
||||||
|
|
@ -602,32 +607,12 @@ static int dircache_do_rebuild(void)
|
||||||
{
|
{
|
||||||
if (fat_ismounted(i))
|
if (fat_ismounted(i))
|
||||||
{
|
{
|
||||||
#endif
|
|
||||||
#ifdef SIMULATOR
|
|
||||||
pdir = opendir_uncached("/");
|
|
||||||
if (pdir == NULL)
|
|
||||||
{
|
|
||||||
logf("Failed to open rootdir");
|
|
||||||
dircache_initializing = false;
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#ifdef HAVE_MULTIVOLUME
|
|
||||||
if ( fat_opendir(IF_MV2(i,) &dir, 0, NULL) < 0 ) {
|
|
||||||
#else
|
|
||||||
if ( fat_opendir(IF_MV2(0,) &dir, 0, NULL) < 0 ) {
|
|
||||||
#endif /* HAVE_MULTIVOLUME */
|
|
||||||
logf("Failed opening root dir");
|
|
||||||
dircache_initializing = false;
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
pdir = &dir;
|
|
||||||
#endif
|
#endif
|
||||||
cpu_boost(true);
|
cpu_boost(true);
|
||||||
#ifdef HAVE_MULTIVOLUME
|
#ifdef HAVE_MULTIVOLUME
|
||||||
if (dircache_travel(IF_MV2(i,) pdir, append_position) < 0)
|
if (dircache_scan_and_build(IF_MV2(i,) append_position) < 0)
|
||||||
#else
|
#else
|
||||||
if (dircache_travel(IF_MV2(0,) pdir, dircache_root) < 0)
|
if (dircache_scan_and_build(IF_MV2(0,) dircache_root) < 0)
|
||||||
#endif /* HAVE_MULTIVOLUME */
|
#endif /* HAVE_MULTIVOLUME */
|
||||||
{
|
{
|
||||||
logf("dircache_travel failed");
|
logf("dircache_travel failed");
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ extern int fat_rename(struct fat_file* file,
|
||||||
long size, int attr);
|
long size, int attr);
|
||||||
|
|
||||||
extern int fat_opendir(IF_MV2(int volume,)
|
extern int fat_opendir(IF_MV2(int volume,)
|
||||||
struct fat_dir *ent, unsigned long currdir,
|
struct fat_dir *ent, unsigned long startcluster,
|
||||||
const struct fat_dir *parent_dir);
|
const struct fat_dir *parent_dir);
|
||||||
extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry);
|
extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry);
|
||||||
extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */
|
extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue