本帖最后由 vsfopen 于 2018-8-20 15:59 编辑
fatfs的移植有2种方法:
1. 使用VSF种的mal块设备,通过独立堆栈的任务,直接调用fatfs的API来操作文件系统。
2. 把fatfs移植到VSF种的文件系统框架下,是的可以通过vsf的文件系统接口,通过fatfs来操作文件系统
第一种方式相对简单,指需要实现fatfs移植的几个口disk相关的操作接口即可:
static struct vsf_fatfs_internal_t
{
struct
{
struct vsfmal_t *mal;
#if FF_FS_REENTRANT
struct vsfsm_crit_t crit;
#endif
} disk[VSF_FATFS_CFG_MAXVOLUME];
uint32_t mskarr[(VSF_FATFS_CFG_MAXVOLUME + 31) >> 5];
} vsf_fatfs;
DSTATUS disk_status(BYTE pdrv)
{
if (pdrv >= VSF_FATFS_CFG_MAXVOLUME) return STA_NOINIT;
return mskarr_get(vsf_fatfs.mskarr, pdrv) ? 0 : STA_NOINIT;
}
DSTATUS disk_initialize(BYTE pdrv)
{
if (disk_status(pdrv))
return STA_NOINIT;
else
{
struct vsfmal_t *mal = vsf_fatfs.disk[pdrv].mal;
struct vsfsm_pt_t pt =
{
.sm = &(vsfsm_thread_get_cur())->sm,
.state = 0,
.user_data = mal,
};
vsfsm_evt_t evt = VSFSM_EVT_NONE;
vsf_err_t err;
while (1)
{
err = vsfmal_init(&pt, evt);
if (!err) break;
else if (err < 0) return STA_NOINIT;
else evt = vsfsm_thread_wait();
}
}
return FR_OK;
}
DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
{
if (disk_status(pdrv))
return RES_PARERR;
else
{
struct vsfmal_t *mal = vsf_fatfs.disk[pdrv].mal;
struct vsfsm_pt_t pt =
{
.sm = &(vsfsm_thread_get_cur())->sm,
.state = 0,
.user_data = mal,
};
vsfsm_evt_t evt = VSFSM_EVT_NONE;
vsf_err_t err;
sector *= mal->cap.block_size;
count *= mal->cap.block_size;
while (1)
{
err = vsfmal_read(&pt, evt, sector, buff, count);
if (!err) break;
else if (err < 0) return STA_NOINIT;
else evt = vsfsm_thread_wait();
}
}
return FR_OK;
}
int vsf_fatfs_add_disk(struct vsfmal_t *mal)
{
uint8_t origlevel = vsfsm_sched_lock();
int idx = mskarr_ffz(vsf_fatfs.mskarr, VSF_FATFS_CFG_MAXVOLUME);
if (idx >= 0)
{
mskarr_set(vsf_fatfs.mskarr, idx);
vsf_fatfs.disk[idx].mal = mal;
}
vsfsm_sched_unlock(origlevel);
return idx;
}
void vsf_fatfs_remove_disk(struct vsfmal_t *mal)
{
for (int i = 0; i < VSF_FATFS_CFG_MAXVOLUME; i++)
{
if (mskarr_get(vsf_fatfs.mskarr, i) && (mal == vsf_fatfs.disk[i].mal))
{
mskarr_clr(vsf_fatfs.mskarr, i);
break;
}
}
}
通过vsf_fatfs_add_disk把VSF种的块设备,注册到vsf_fatfs模块中,并且得到对应的index序号,就可以通过序号来访问块设备里的FAT文件系统了。
由于disk_initialize的接口是阻塞的,所以应用层必须是vsfsm_thread_t的独立堆栈任务,disk_initialize里,也只是调用了PT方式的块设备驱动接口。
第二种相对麻烦一些,因为fatfs这种文件系统的应用层API过于高层,并不适合vsf的底层文件系统驱动接口。不过,可以使用fatfs的FF_FS_RPATH来实现vsf底层文件系统接口需要的相对路径功能。
void vsf_fatfs_run_mount(struct vsfsm_thread_t *thread)
{
struct vsf_fatfs_t *fatfs = (struct vsf_fatfs_t *)thread->priv;
int idx = vsf_fatfs_add_disk(fatfs->mal);
char volume[3];
if (idx < 0)
return;
volume[0] = '0' + idx;
volume[1] = ':';
volume[2] = '\0';
if (f_mount(&__fatfs, volume, 1) || f_opendir(&fatfs->root, volume))
fatfs->root.obj.fs = NULL;
}
static vsf_err_t vsf_fatfs_mount(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
struct vsfile_t *dir)
{
struct vsf_fatfs_t *fatfs = (struct vsf_fatfs_t *)pt->user_data;
vsfsm_pt_begin(pt);
vsfsm_crit_init(&fatfs->crit, VSFSM_EVT_USER);
vsfsm_crit_enter(&fatfs->crit, pt->sm);
fatfs->thread.op.on_terminate = vsf_fatfs_on_finish;
fatfs->thread.stack_size = sizeof(fatfs->stack);
fatfs->thread.stack = fatfs->stack;
fatfs->pending_sm = pt->sm;
fatfs->thread.priv = fatfs;
fatfs->thread.op.on_run = vsf_fatfs_run_mount;
vsfsm_thread_start(&fatfs->thread);
vsfsm_pt_wfe(pt, VSFSM_EVT_USER);
vsfsm_crit_leave(&fatfs->crit);
vsfsm_pt_end(pt);
return !fatfs->root.obj.fs ? VSFERR_FAIL : VSFERR_NONE;
}
void vsf_fatfs_run_getchild(struct vsfsm_thread_t *thread)
{
struct vsf_fatfs_t *fatfs = (struct vsf_fatfs_t *)thread->priv;
char *name = fatfs->getchild.name;
DIR *dir = fatfs->getchild.dir;
FILINFO fno;
fatfs->getchild.file = NULL;
while (1)
{
if (f_readdir(dir, &fno) || !fno.fname[0])
break;
if (vsfile_match(name, fno.fname))
{
struct vsf_fatfs_file_t *file;
uint32_t fname_len = strlen(fno.fname) + 1;
uint32_t entry_len = fno.fattrib & AM_DIR ? sizeof(DIR) : sizeof(FIL);
uint32_t size = sizeof(*file) + entry_len + fname_len;
file = vsf_bufmgr_malloc(size);
if (file != NULL)
{
file->entry.ptr = &file[1];
file->file.name = (char *)file->entry.ptr + entry_len;
strcpy(file->file.name, fno.fname);
file->file.attr = fno.fattrib;
file->file.op = &vsf_fatfs_fsop;
file->file.size = fno.fsize;
__fatfs.cdir = dir->obj.sclust;
if (file->file.attr & VSFILE_ATTR_DIRECTORY)
{
DIR *dp = file->entry.d;
if (f_opendir(dp, file->file.name))
return;
}
else
{
FIL *fp = file->entry.f;
if (f_open(fp, file->file.name, 1))
return;
}
fatfs->getchild.file = file;
}
break;
}
}
}
static vsf_err_t vsf_fatfs_getchild(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
struct vsfile_t *dir, char *name, uint32_t idx, struct vsfile_t **file)
{
struct vsf_fatfs_t *fatfs = (struct vsf_fatfs_t *)pt->user_data;
vsfsm_pt_begin(pt);
if (vsfsm_crit_enter(&fatfs->crit, pt->sm))
vsfsm_pt_wfe(pt, VSFSM_EVT_USER);
fatfs->getchild.dir = ((struct vsf_fatfs_file_t *)dir)->entry.d;
if (!fatfs->getchild.dir)
fatfs->getchild.dir = &fatfs->root;
fatfs->getchild.name = name;
fatfs->pending_sm = pt->sm;
fatfs->thread.priv = fatfs;
fatfs->thread.op.on_run = vsf_fatfs_run_getchild;
vsfsm_thread_start(&fatfs->thread);
vsfsm_pt_wfe(pt, VSFSM_EVT_USER);
*file = &fatfs->getchild.file->file;
vsfsm_crit_leave(&fatfs->crit);
vsfsm_pt_end(pt);
return VSFERR_NONE;
}
void vsf_fatfs_run_read(struct vsfsm_thread_t *thread)
{
struct vsf_fatfs_t *fatfs = (struct vsf_fatfs_t *)thread->priv;
struct vsf_fatfs_file_t *file = fatfs->close.file;
fatfs->rw.rw_size = 0;
if (!f_lseek(file->entry.f, fatfs->rw.offset))
f_read(file->entry.f, fatfs->rw.buff, fatfs->rw.size, &fatfs->rw.rw_size);
}
static vsf_err_t vsf_fatfs_read(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
struct vsfile_t *file, uint64_t offset, uint32_t size, uint8_t *buff,
uint32_t *rsize)
{
struct vsf_fatfs_t *fatfs = (struct vsf_fatfs_t *)pt->user_data;
vsfsm_pt_begin(pt);
if (vsfsm_crit_enter(&fatfs->crit, pt->sm))
vsfsm_pt_wfe(pt, VSFSM_EVT_USER);
fatfs->rw.file = (struct vsf_fatfs_file_t *)file;
fatfs->rw.buff = buff;
fatfs->rw.offset = offset;
fatfs->rw.size = size;
fatfs->pending_sm = pt->sm;
fatfs->thread.priv = fatfs;
fatfs->thread.op.on_run = vsf_fatfs_run_read;
vsfsm_thread_start(&fatfs->thread);
vsfsm_pt_wfe(pt, VSFSM_EVT_USER);
if (rsize != NULL)
*rsize = fatfs->rw.rw_size;
vsfsm_crit_leave(&fatfs->crit);
vsfsm_pt_end(pt);
return VSFERR_NONE;
}
vsf_fatfs_mount、vsf_fatfs_getchild、vsf_fatfs_read等等接口是VSF的文件系统驱动的接口,是PT形式的任务。由于fatfs需要使用vsfsm_thread_t形式的任务,所以在这些接口里,只是简单的设置了参数,并且启动对应的vsfsm_thread_t任务,并等待执行完成,返回结果。
其中,mount和读写等接口相对简单,直接调用fatfs对应的API即可。不过,vsf的文件系统有一个getchild接口,用于得到指定目录下,指定的文件或者子目录。这个接口就需要相对的路径,fatfs正好可以通过FF_FS_RPATH来使能相对路径,但是f_chdir接口仍旧需要绝对路径,所以i这里就只是简单的通过设置fatfs.cdir来设置当前的目录,然后就后面的路径就是基于这个当前目录的相对路径。
注意:一些中间参数和变量(包括vsfsm_thread_t)会放在fatfs的结构中,所以,调用这些接口的时候,需要保证其他任务不对调用,以避免资源使用的冲突,这里使用了一个vsfsm_crit_t来避免这个问题。所以,所有这些PT的文件系统接口,会先做保护,然后再使用共享资源。
|