/*-----------------------------------------------------------------------*/
/* Mount/Unmount a Logical Drive */
/*-----------------------------------------------------------------------*/
FRESULT f_mount (
FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/
const TCHAR* path, /* Logical drive number to be mounted/unmounted */
BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */
)
{
FATFS *cfs;
int vol;
FRESULT res;
const TCHAR *rp = path;
/* Get volume ID (logical drive number) */
vol = get_ldnumber(&rp);
if (vol < 0) return FR_INVALID_DRIVE;
cfs = FatFs[vol]; /* Pointer to the filesystem object of the volume */
if (cfs) { /* Unregister current filesystem object if regsitered */
FatFs[vol] = 0;
#if FF_FS_LOCK
clear_share(cfs);
#endif
#if FF_FS_REENTRANT /* Discard mutex of the current volume */
ff_mutex_delete(vol);
#endif
cfs->fs_type = 0; /* Invalidate the filesystem object to be unregistered */
}
if (fs) { /* Register new filesystem object */
fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */
#if FF_FS_REENTRANT /* Create a volume mutex */
fs->ldrv = (BYTE)vol; /* Owner volume ID */
if (!ff_mutex_create(vol)) return FR_INT_ERR;
#if FF_FS_LOCK
if (SysLock == 0) { /* Create a system mutex if needed */
if (!ff_mutex_create(FF_VOLUMES)) {
ff_mutex_delete(vol);
return FR_INT_ERR;
}
SysLock = 1; /* System mutex is ready */
}
#endif
#endif
fs->fs_type = 0; /* Invalidate the new filesystem object */
FatFs[vol] = fs; /* Register new fs object */
}
if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted in subsequent file functions */
res = mount_volume(&path, &fs, 0); /* Force mounted the volume */
LEAVE_FF(fs, res);
}
f_mount
是 FATFS 文件系统库中的核心函数,用于 挂载(注册)或卸载(注销)逻辑驱动器 的文件系统对象。具体功能包括:
挂载文件系统:将 FATFS
对象与逻辑驱动器关联,并初始化文件系统。
卸载文件系统:解除当前关联的 FATFS
对象,释放资源。
延迟挂载:支持注册文件系统但不立即初始化(需后续文件操作触发)。
参数 | 类型 | 说明 |
---|---|---|
fs |
FATFS* |
要注册的文件系统对象指针(NULL 表示卸载当前文件系统)。 |
path |
const TCHAR* |
逻辑驱动器路径(如 "0:" ),用于确定驱动器号。 |
opt |
BYTE |
挂载选项:0 =延迟挂载(后续操作触发初始化),1 =立即挂载。 |
vol = get_ldnumber(&rp); // 解析路径,获取逻辑驱动器号(如 "0:"→0)
if (vol < 0) return FR_INVALID_DRIVE;
get_ldnumber
解析路径中的逻辑驱动器号(例如 "0:"
对应 vol=0
)。
无效路径返回 FR_INVALID_DRIVE
。
cfs = FatFs[vol]; /* Pointer to the filesystem object of the volume */
if (cfs) { /* Unregister current filesystem object if regsitered */
FatFs[vol] = 0;
#if FF_FS_LOCK
clear_share(cfs);
#endif
#if FF_FS_REENTRANT /* Discard mutex of the current volume */
ff_mutex_delete(vol);
#endif
cfs->fs_type = 0; /* Invalidate the filesystem object to be unregistered */
}
若当前驱动器已挂载文件系统,先释放相关资源并注销。
fs
非空) if (fs) { /* Register new filesystem object */
fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */
#if FF_FS_REENTRANT /* Create a volume mutex */
fs->ldrv = (BYTE)vol; /* Owner volume ID */
if (!ff_mutex_create(vol)) return FR_INT_ERR;
#if FF_FS_LOCK
if (SysLock == 0) { /* Create a system mutex if needed */
if (!ff_mutex_create(FF_VOLUMES)) {
ff_mutex_delete(vol);
return FR_INT_ERR;
}
SysLock = 1; /* System mutex is ready */
}
#endif
#endif
fs->fs_type = 0; /* Invalidate the new filesystem object */
FatFs[vol] = fs; /* Register new fs object */
}
将新的 FATFS
对象关联到逻辑驱动器,初始化同步机制。
opt == 1
)if (opt == 0) return FR_OK; // 延迟挂载,直接返回
res = mount_volume(&path, &fs, 0); // 调用底层函数初始化文件系统
LEAVE_FF(fs, res); // 返回结果(可能释放互斥锁)
若选择立即挂载,调用 mount_volume
初始化文件系统(读取引导扇区、FAT表等)。
文件锁定(FF_FS_LOCK
):
清理文件的共享访问控制(例如多线程同时访问文件时的互斥)。
互斥锁(FF_FS_REENTRANT
):
确保多线程环境下对文件系统的原子操作:
ff_mutex_delete(vol)
:删除驱动器级锁。
ff_mutex_create(vol)
:创建驱动器级锁。
SysLock
:系统级全局锁,用于保护共享资源(如 FatFs
数组)。
若 opt=0
,仅注册 FATFS
对象但不初始化,首次文件操作(如 f_open
)时触发初始化。
分层架构:
通过 FatFs[vol]
全局数组管理所有逻辑驱动器的文件系统对象,实现驱动器与文件系统的解耦。
条件编译:
使用 FF_FS_LOCK
、FF_FS_REENTRANT
等宏控制功能模块,增强代码可移植性。
引用计数与无效标记:
通过 fs->fs_type = 0
标记文件系统对象无效,防止误用已卸载的对象。
互斥锁生命周期:
在注册/注销时动态创建/删除互斥锁,避免资源泄漏。
// 挂载逻辑驱动器 0
FATFS fs;
f_mount(&fs, "0:", 1); // 立即挂载
// 卸载逻辑驱动器 0
f_mount(NULL, "0:", 0); // 卸载
核心功能:管理逻辑驱动器的挂载与卸载,关联文件系统对象。
关键机制:资源清理、互斥锁、延迟初始化。
适用场景:嵌入式系统中动态挂载存储设备(如 SD 卡、USB 设备)。
扩展性:通过条件编译支持多种配置(如多线程安全、文件锁定)。
通过此函数,FATFS 实现了灵活的文件系统管理,适用于资源受限的嵌入式环境。
Get logical drive number from path name
static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
const TCHAR** path /* Pointer to pointer to the path name */
)
{
const TCHAR *tp;
const TCHAR *tt;
TCHAR tc;
int i;
int vol = -1;
#if FF_STR_VOLUME_ID /* Find string volume ID */
const char *sp;
char c;
#endif
tt = tp = *path;
if (!tp) return vol; /* Invalid path name? */
do { /* Find a colon in the path */
tc = *tt++;
} while (!IsTerminator(tc) && tc != ':');
if (tc == ':') { /* DOS/Windows style volume ID? */
i = FF_VOLUMES;
if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric volume ID + colon? */
i = (int)*tp - '0'; /* Get the LD number */
}
#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
else {
i = 0;
do {
sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
do { /* Compare the volume ID with path name */
c = *sp++; tc = *tp++;
if (IsLower(c)) c -= 0x20;
if (IsLower(tc)) tc -= 0x20;
} while (c && (TCHAR)c == tc);
} while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */
}
#endif
if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
vol = i; /* Drive number */
*path = tt; /* Snip the drive prefix off */
}
return vol;
}
#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
if (*tp == '/') { /* Is there a volume ID? */
while (*(tp + 1) == '/') tp++; /* Skip duplicated separator */
i = 0;
do {
tt = tp; sp = VolumeStr[i]; /* Path name and this string volume ID */
do { /* Compare the volume ID with path name */
c = *sp++; tc = *(++tt);
if (IsLower(c)) c -= 0x20;
if (IsLower(tc)) tc -= 0x20;
} while (c && (TCHAR)c == tc);
} while ((c || (tc != '/' && !IsTerminator(tc))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */
if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
vol = i; /* Drive number */
*path = tt; /* Snip the drive prefix off */
}
return vol;
}
#endif
/* No drive prefix is found */
#if FF_FS_RPATH != 0
vol = CurrVol; /* Default drive is current drive */
#else
vol = 0; /* Default drive is 0 */
#endif
return vol; /* Return the default drive */
}
Determine logical drive number and mount the volume if needed
static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
const TCHAR** path, /* Pointer to pointer to the path name (drive number) */
FATFS** rfs, /* Pointer to pointer to the found filesystem object */
BYTE mode /* Desiered access mode to check write protection */
)
{
int vol;
FATFS *fs;
DSTATUS stat;
LBA_t bsect;
DWORD tsect, sysect, fasize, nclst, szbfat;
WORD nrsv;
UINT fmt;
/* Get logical drive number */
*rfs = 0;
vol = get_ldnumber(path);
if (vol < 0) return FR_INVALID_DRIVE;
/* Check if the filesystem object is valid or not */
fs = FatFs[vol]; /* Get pointer to the filesystem object */
if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */
#if FF_FS_REENTRANT
if (!lock_volume(fs, 1)) return FR_TIMEOUT; /* Lock the volume, and system if needed */
#endif
*rfs = fs; /* Return pointer to the filesystem object */
mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */
if (fs->fs_type != 0) { /* If the volume has been mounted */
stat = disk_status(fs->pdrv);
if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */
return FR_WRITE_PROTECTED;
}
return FR_OK; /* The filesystem object is already valid */
}
}
/* The filesystem object is not valid. */
/* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */
fs->fs_type = 0; /* Invalidate the filesystem object */
stat = disk_initialize(fs->pdrv); /* Initialize the volume hosting physical drive */
if (stat & STA_NOINIT) { /* Check if the initialization succeeded */
return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */
}
if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */
return FR_WRITE_PROTECTED;
}
#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;
if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
#endif
/* Find an FAT volume on the hosting drive */
fmt = find_volume(fs, LD2PT(vol));
if (fmt == 4) return FR_DISK_ERR; /* An error occurred in the disk I/O layer */
if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */
bsect = fs->winsect; /* Volume offset in the hosting physical drive */
/* An FAT volume is found (bsect). Following code initializes the filesystem object */
#if FF_FS_EXFAT
if (fmt == 1) {
QWORD maxlba;
DWORD so, cv, bcl, i;
for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */
if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
return FR_NO_FILESYSTEM;
}
maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA of the volume + 1 */
if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be accessed in 32-bit LBA) */
fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */
fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */
if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */
fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */
if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768 sectors) */
nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */
if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */
fs->n_fatent = nclst + 2;
/* Boundaries and Limits */
fs->volbase = bsect;
fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size required) */
fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
/* Get bitmap location and check if it is contiguous (implementation assumption) */
so = i = 0;
for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */
if (i == 0) {
if (so >= fs->csize) return FR_NO_FILESYSTEM; /* Not found? */
if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR;
so++;
}
if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */
i = (i + SZDIRE) % SS(fs); /* Next entry */
}
bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */
if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; /* (Wrong cluster#) */
fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */
for (;;) { /* Check if bitmap is contiguous */
if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR;
cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4);
if (cv == 0xFFFFFFFF) break; /* Last link? */
if (cv != ++bcl) return FR_NO_FILESYSTEM; /* Fragmented bitmap? */
}
#if !FF_FS_READONLY
fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */
#endif
fmt = FS_EXFAT; /* FAT sub-type */
} else
#endif /* FF_FS_EXFAT */
{
if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */
fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */
if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);
fs->fsize = fasize;
fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */
if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
fasize *= fs->n_fats; /* Number of sectors for FAT area */
fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */
if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */
fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */
if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */
tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */
if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */
if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */
/* Determine the FAT sub type */
sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */
if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
nclst = (tsect - sysect) / fs->csize; /* Number of clusters */
if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
fmt = 0;
if (nclst <= MAX_FAT32) fmt = FS_FAT32;
if (nclst <= MAX_FAT16) fmt = FS_FAT16;
if (nclst <= MAX_FAT12) fmt = FS_FAT12;
if (fmt == 0) return FR_NO_FILESYSTEM;
/* Boundaries and Limits */
fs->n_fatent = nclst + 2; /* Number of FAT entries */
fs->volbase = bsect; /* Volume start sector */
fs->fatbase = bsect + nrsv; /* FAT start sector */
fs->database = bsect + sysect; /* Data start sector */
if (fmt == FS_FAT32) {
if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */
if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */
szbfat = fs->n_fatent * 4; /* (Needed FAT size) */
} else {
if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */
fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
}
if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */
#if !FF_FS_READONLY
/* Get FSInfo if available */
fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */
fs->fsi_flag = 0x80;
#if (FF_FS_NOFSINFO & 3) != 3
if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
&& ld_word(fs->win + BPB_FSInfo32) == 1
&& move_window(fs, bsect + 1) == FR_OK)
{
fs->fsi_flag = 0;
if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */
&& ld_dword(fs->win + FSI_LeadSig) == 0x41615252
&& ld_dword(fs->win + FSI_StrucSig) == 0x61417272)
{
#if (FF_FS_NOFSINFO & 1) == 0
fs->free_clst = ld_dword(fs->win + FSI_Free_Count);
#endif
#if (FF_FS_NOFSINFO & 2) == 0
fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);
#endif
}
}
#endif /* (FF_FS_NOFSINFO & 3) != 3 */
#endif /* !FF_FS_READONLY */
}
fs->fs_type = (BYTE)fmt;/* FAT sub-type (the filesystem object gets valid) */
fs->id = ++Fsid; /* Volume mount ID */
#if FF_USE_LFN == 1
fs->lfnbuf = LfnBuf; /* Static LFN working buffer */
#if FF_FS_EXFAT
fs->dirbuf = DirBuf; /* Static directory block scratchpad buuffer */
#endif
#endif
#if FF_FS_RPATH != 0
fs->cdir = 0; /* Initialize current directory */
#endif
#if FF_FS_LOCK /* Clear file lock semaphores */
clear_share(fs);
#endif
return FR_OK;
}