[FAT文件系统]-驱动框架设计

本文详细介绍了驱动框架设计,包括代码结构分层的好处、层次划分(测试层、fat层、disk层),以及如何实现虚拟磁盘驱动,涉及xdisk、driver.c和xfat_test.c中的关键函数。着重展示了如何通过分层设计提高代码组织和模块化,以及磁盘操作接口的抽象和使用示例。
摘要由CSDN通过智能技术生成

驱动框架设计

§ 代码结构分层

  • 代码结构分层好处
    • 有组织的安排代码编写的文件;
    • 分层结构能简化代码开发;
  • 代码分层设计:测试层 -> fat层 -> disk层
    • 测试层:使用文件读写接口;实现文件的读写测试;
    • fat层:使用设备读写接口;实现设备上FAT数据的解析;实现文件读写接口,如file_read等;
    • disk层:抽象存储设备结构;包含设备属性及相关操作;
  • 实现原理
    • 设备特性:块大小、块数量
    • 操作接口:打开、关闭、块的读取、块的写入
  • 具体实现
    • 定义基本数据类型和错误码;
    • 定义设备类结构,定义操作接口结构;
    • 添加驱动实例;
  • 分层目录

xfat_test.c ---- xfat.c ---- xdisk.c ---- driver.c

§ 实现虚拟磁盘驱动

  • 具体实现
    • 修改xdisk结构;
    • 使用C接口实现打开、关闭、读取、写入接口;
    • 添加测试代码;
  • 驱动实现
    • 主要完成:xdisk_hw_open,xdisk_hw_close,xdisk_hw_read_sector,xdisk_hw_write_sector四个函数开发;

1. 驱动程序实现

/* driver.c */

#include <stdio.h>
#include <assert.h>
#include "xdisk.h"
#include "xfat.h"

static xfat_err_t xdisk_hw_open(struct _xdisk_t* disk, void* init_data)
{
	assert(disk);
	assert(init_data);

	const char* path = (const char*)init_data;
	FILE* file = fopen(path, "rb+");

	if (NULL == file)
	{
		printf("open disk failed: %s\n", path);
		return FS_ERR_IO;
	}

	disk->data = file;
	disk->sector_size = 512;
	
	fseek(file, 0, SEEK_END);
	disk->total_sector = ftell(file) / disk->sector_size;

	return FS_ERR_OK;
}

static xfat_err_t xdisk_hw_close(struct _xdisk_t* disk)
{
	assert(disk);
	FILE* file = (FILE*)disk->data;
	fclose(file);
	return FS_ERR_OK;
}
static xfat_err_t xdisk_hw_read_sector(struct _xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count)
{
	assert(disk);
	assert(buffer);

	u32_t offset = start_sector * disk->sector_size;
	FILE* file = (FILE*)disk->data;

	int err = fseek(file, offset, SEEK_SET);
	if (err == -1)
	{
		printf("seek disk failed: 0x%x\n", offset);
		return FS_ERR_IO;
	}

	err = fread(buffer, disk->sector_size, count, file);
	if (err == -1)
	{
		printf("read disk failed: sector : %d, count:%d\n", start_sector, count);
		return FS_ERR_IO;
	}

	return FS_ERR_OK;
}
static xfat_err_t xdisk_hw_write_sector(struct _xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count)
{
	assert(disk);
	assert(buffer);

	u32_t offset = start_sector * disk->sector_size;
	FILE* file = (FILE*)disk->data;

	int err = fseek(file, offset, SEEK_SET);
	if (err == -1)
	{
		printf("seek disk failed: 0x%x\n", offset);
		return FS_ERR_IO;
	}

	err = fwrite(buffer, disk->sector_size, count, file);
	if (err == -1)
	{
		printf("write disk failed: sector : %d, count:%d\n", start_sector, count);
		return FS_ERR_IO;
	}

	fflush(file);
	return FS_ERR_OK;
}


xdisk_driver_t vdisk_driver = {
	.open = xdisk_hw_open,
	.close = xdisk_hw_close,
	.read_sector = xdisk_hw_read_sector,
	.write_sector = xdisk_hw_write_sector
};

其中C库IO操作函数(<stdio.h>)说明如下(摘自菜鸟教程):

  • fopen()
    • FILE *fopen(const char *filename, const char *mode)
    • C 库函数 FILE *fopen(const char *filename, const char *mode) 使用给定的模式 mode 打开 filename 所指向的文件。q其中mode值如下表所示:
模式描述
“r”打开一个用于读取的文件。该文件必须存在。
“w”创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
“a”追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
“r+”打开一个用于更新的文件,可读取也可写入。该文件必须存在。
“w+”创建一个用于读写的空文件。
“a+”打开一个用于读取和追加的文件。
  • fseek()
    • int fseek(FILE *stream, long int offset, int whence)
    • C 库函数 int fseek(FILE *stream, long int offset, int whence) 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。其中whence参数值如下所示:
常量描述
SEEK_SET文件的开头
SEEK_CUR文件指针的当前位置
SEEK_END文件的末尾
  • ftell()
    • long int ftell(FILE *stream)
    • C 库函数 long int ftell(FILE *stream) 返回给定流 stream 的当前文件位置。

代码中依次使用:

fseek(file, 0, SEEK_END);
disk->total_sector = ftell(file) / disk->sector_size;

首先使用fseek将偏移值移动到文件末尾,然后使用ftell获得文件末尾的偏移值,除以扇区大小后获得该文件有共有多少个扇区;

  • fclose()

    • int fclose(FILE *stream)
    • C 库函数 int fclose(FILE *stream) 关闭流 stream。刷新所有的缓冲区。
  • fread()

    • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
    • C 库函数 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中;
  • fwrite()

    • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
    • C 库函数 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)ptr 所指向的数组中的数据写入到给定流 stream 中.
  • fflush()

    • int fflush(FILE *stream)
    • C 库函数 int fflush(FILE *stream) 刷新流 stream 的输出缓冲区。

    驱动写代码中使用该函数是为了确保写入内容已经刷新到文件中;避免保存在程序的缓冲区中;

2. 测试程序实现

/* xfat_test.c */

#include <stdio.h>
#include <string.h>
#include "xfat.h"
#include "xdisk.h"

extern xdisk_driver_t vdisk_driver;

const char* disk_path_test = "disk_test.img";
u32_t read_buffer[160 * 1024];
u32_t write_buffer[160 * 1024];

int disk_io_test(void)
{
	int err;

	xdisk_t disk_test;

	disk_test.driver = &vdisk_driver;
	memset(read_buffer, 0, sizeof(read_buffer));

	err = disk_test.driver->open(&disk_test, disk_path_test);
	if (err)
	{
		printf("open disk failed!\n");
		return -1;
	}

	err = disk_test.driver->write_sector(&disk_test, (u8_t*)write_buffer, 0, 2);
	if (err)
	{
		printf("write disk failed!\n");
		return -1;
	}

	err = disk_test.driver->read_sector(&disk_test, (u8_t*)read_buffer, 0, 2);
	if (err)
	{
		printf("write disk failed!\n");
		return -1;
	}

	err = memcmp((u8_t*)read_buffer, (u8_t*)write_buffer, disk_test.sector_size * 2);
	if (err != 0)
	{
		printf("data not equal!\n");
		return -1;
	}

	err = disk_test.driver->close(&disk_test);
	if (err)
	{
		printf("disk close failed!\n");
		return -1;
	}

	printf("disk io test ok!\n");

	return 0;
}



int main(void)
{
	int err;

	for (int i = 0; i < sizeof(write_buffer) / sizeof(u32_t); i++)
	{
		write_buffer[i] = i;
	}

	err = disk_io_test();
	if (err)
		return err;

	printf("test end");

	return 0;
}

另外说明一下disk_test.img文件如何创建:

  • img文件说明(摘自维基百科)

IMG是一种文件归档格式(archive format),主要是为了建立磁盘的映像文件(disk image),它可以用来封装存储整个磁盘(通常指软磁盘,Floppy Disk或Diskette)或整片光盘的内容,使用".IMG"这个扩展名的文件就是利用这种文件格式来建立的。

.IMG这个文件格式可视为.ISO格式的一种超集合。由于.ISO只能封存使用ISO9660和UDF这两种文件系统的存储介质,意即.ISO只能拿来封存CD或DVD,因此才发展出了.IMG,它是以.ISO格式为基础另外新增可封存使用其它文件系统的存储介质的能力,.IMG可向后兼容于.ISO,如果是拿来封存CD或DVD,则使用.IMG和.ISO这两种格式所产生出来的内容是一样的。

不要将这个文件格式误认为是麦金塔磁盘映像档(Macintosh Disk Image),麦金塔磁盘映像档是由Aladdin Systems(现在称为Allume Systems)ShrinkWrap与Apple Disk Copy for Mac OS这两个工具软件所共享的扩展名。

  • 创建
    • 该测试代码中使用img文件作为目标磁盘操作,是为了降低成本:不需要额外硬件、又避免弄坏系统;
    • 生成img文件可使用软件DiskGenius,操作步骤可参考链接:创建.img 磁盘映像 - DiskGenius
    • 需要注意的是代码中使用相对路径存放img文件,需放在工作目录下。获修改工作目录,以VS为例,查询工作目录可通过以下方式:右击项目文件->属性->配置属性->调试->工作目录->点击下拉窗->编辑;即可显示工作目录的绝对路径;

§ 实现磁盘操作接口

  • 目标

    • 最好是专门提供一组disk层的接口给上层使用,而不是暴露更底层的driver层接口;
    • 不方便进行功能拓展,如磁盘读写缓存;
    • 上一章节中应用层读写disk使用的是driver层接口,通过在disk层进行隔离,实现解耦的目的;

1. disk层实现

xdisk.h如下;

#ifndef  __XDISK_H
#define __XDISK_H

#include "xtypes.h"

struct _xdisk_t;

typedef struct _xdisk_driver_t {
	xfat_err_t(*open)(struct _xdisk_t* disk, void* init_data);
	xfat_err_t(*close)(struct _xdisk_t* disk);
	xfat_err_t(*read_sector)(struct _xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count);
	xfat_err_t(*write_sector)(struct _xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count);

}xdisk_driver_t;

typedef struct _xdisk_t {
	const char* name;
	u32_t sector_size;
	u32_t total_sector;
	xdisk_driver_t* driver;
	void* data;
}xdisk_t;

xfat_err_t xdisk_open(xdisk_t* disk, const char* name, xdisk_t*driver, void* init_data);
xfat_err_t xdisk_close(xdisk_t* disk);
xfat_err_t xdisk_read_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count);
xfat_err_t xdisk_write_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count);


#endif //  __XDISK_H

xdisk.c如下;

#include "xdisk.h"

xfat_err_t xdisk_open(xdisk_t* disk, const char* name, xdisk_t*driver, void* init_data)
{
	xfat_err_t err;

	disk->driver = driver;
	err = disk->driver->open(disk, init_data);

	return err;
}

xfat_err_t xdisk_close(xdisk_t* disk)
{
	xfat_err_t err;

	err = disk->driver->close(disk);

	return err;
}


xfat_err_t xdisk_read_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count)
{
	xfat_err_t err;

	if (start_sector + count >= disk->total_sector)
	{
		return FS_ERR_PARAM;
	}

	err = disk->driver->read_sector(disk, buffer, start_sector, count);

	return err;
}


xfat_err_t xdisk_write_sector(xdisk_t* disk, u8_t* buffer, u32_t start_sector, u32_t count)
{
	xfat_err_t err;

	if (start_sector + count >= disk->total_sector)
	{
		return FS_ERR_PARAM;
	}

	err = disk->driver->write_sector(disk, buffer, start_sector, count);

	return err;
}

2. 测试程序修改

xfat_test.c修改如下;

#include <stdio.h>
#include <string.h>
#include "xfat.h"
#include "xdisk.h"


extern xdisk_driver_t vdisk_driver;

const char* disk_path_test = "disk_test.img";
u32_t read_buffer[160 * 1024];
u32_t write_buffer[160 * 1024];

int disk_io_test(void)
{
	int err;

	xdisk_t disk_test;

	memset(read_buffer, 0, sizeof(read_buffer));

	err = xdisk_open(&disk_test, "vdisk", &vdisk_driver, disk_path_test);
	if (err)
	{
		printf("open disk failed!\n");
		return -1;
	}

	err = xdisk_write_sector(&disk_test, (u8_t*)write_buffer, 0, 2);
	if (err)
	{
		printf("write disk failed!\n");
		return -1;
	}

	err = xdisk_read_sector(&disk_test, (u8_t*)read_buffer, 0, 2);
	if (err)
	{
		printf("write disk failed!\n");
		return -1;
	}

	err = memcmp((u8_t*)read_buffer, (u8_t*)write_buffer, disk_test.sector_size * 2);
	if (err != 0)
	{
		printf("data not equal!\n");
		return -1;
	}

	err = xdisk_close(&disk_test);
	if (err)
	{
		printf("disk close failed!\n");
		return -1;
	}

	printf("disk io test ok!\n");

	return 0;
}



int main(void)
{
	int err;

	for (int i = 0; i < sizeof(write_buffer) / sizeof(u32_t); i++)
	{
		write_buffer[i] = i;
	}

	err = disk_io_test();
	if (err)
		return err;

	printf("test end");

	return 0;
}

  • 33
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值