本篇内容主要讲解“linux mtd的概念是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“linux mtd的概念是什么”吧!
在linux中,mtd是指“内存技术设备”,是存储设备中的一个子系统。linux引入MTD系统是为了给NOR FLASH和NAND FLASH设备提供统一接口。MTD设备通常可分为四层:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。
本教程操作环境:linux5.9.8系统、Dell G3电脑。
Linux MTD是什么?
MTD全称“Memory Technology Device”,意思为“内存技术设备”,是Linux的存储设备中的一个子系统。
在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。
设计此MTD系统的目的是,对于内存类的设备,提供一个抽象层,一个接口,使得对于硬件驱动设计者来说,只需要去提供最简单的底层硬件设备的读/写/擦除函数就可以了,数据对于上层使用者来说是如何表示的,可以不关心,因为MTD存储设备子系统都帮你做好了。
MTD框架
Linux的MTD设备位于drivers/mtd/下面。
MTD文件下的内容如下:

MTD设备通常可分为四层
上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。

1.cmdlinepart.c
当mtd分区表由u-boot通过cmd参数传输给linux时,linux内核可以不用对mtdparts进行注册添加,只需要将MTD中的command line partition选项开启即可。使用这种的方法u-boot下需要对MTD进行支持,且所传输的mtd分区参数要符合格式要求。
2.devices文件夹
当我们有一个spi flash设备时且要使用mtd进行管理,我们一般会将其放在devices文件夹下,如devices文件夹下面的m25p80.c就是一个典型的spi flash设备。
3.chips/nand/onenand文件夹
nand flash 驱动在nand文件夹下;
onenand flash 驱动在onenand文件夹下;
nor flash比较杂,下面几个文件下都会有:
chips:cfi/jedec接口通用驱动
devices:nor flash底层驱动(spi flash)
maps:nor flash映射关系相关函数
4.核心文件
mtdchar.c : MTD字符设备接口相关实现,设备号31;
mtdblock.c : MTD块设备接口相关实现,设备号90,;
mtdcore.c: MTD原始设备接口相关实现;
mtdpart.c : MTD分区接口相关实现。
5.ubi
ubifs文件的支持层,当使用ubifs文件系统时,需要将Device Drivers -> Memory Technology Device (MTD) support -> UBI -Unsorted block image 中的Enable UBI选中。
将File systems -> Miscellaneous filesystems中的UBIFS file system support选中。
MTD分区表的实现
在开机过程从console经常可以看到类似以下信息,
0x000000000000-0x000000100000:"Bootloade" 0x000000100000-0x000002000000:"Kernel" 0x000002000000-0x000003000000:"User" 0x000003000000-0x000008000000:"FileSystem"
这就是MTD给我们一种最直观的表示形式,给我们展示了内存中各模块的分区结构,但这些分区是怎样实现的呢?分区表的实现方式有几种,下面进行分别说明:
注:分区表实现的前提是MTD设备驱动已经成功了,否则连驱动都没成功就无分区可说了。
1.内核中添加
在内核中添加这是一个比较经常使用的方法,随便一本驱动移植的书上应该都有,主要就是在平台设备里面添加mtd_partition,添加类似下面的信息,这边就不过多描述
structmtd_partitions3c_nand_part[]={
{
.name="Bootloader",
.offset=0,
.size=(1*SZ_1M),
.mask_flags=MTD_CAP_NANDFLASH,
},
{
.name="Kernel",
.offset=(1*SZ_1M),
.size=(31*SZ_1M),
.mask_flags=MTD_CAP_NANDFLASH,
},
{
.name="User",
.offset=(32*SZ_1M),
.size=(16*SZ_1M),
},
{
.name="FileSystem",
.offset=(48*SZ_1M),
.size=(96*SZ_1M),
}
};
staticstructs3c_nand_sets3c_nand_sets[]={
[0]={
.name="nand",
.nr_chips=1,
.nr_partitions=ARRAY_SIZE(s3c_nand_part),
.partitions=ok6410_nand_part,
},
};
staticstructs3c_platform_nands3c_nand_info={
.tacls=25,
.twrph0=55,
.twrph2=40,
.nr_sets=ARRAY_SIZE(s3c_nand_sets),
.sets=ok6410_nand_sets,
};
staticvoid__inits3c_machine_init(void)
{
s3c_nand_set_platdata(&s3c_nand_info);
}因为我们的MTD驱动已经完成了,当device和driver匹配后会调用驱动中的probe接口函数,我们需要在probe函数里面调用add_mtd_partitions(s3c_mtd, sets->partitions, sets->nr_partitions);实现分区表的添加。
2.u-boot传参
在u-boot下可以通过添加mtdparts信息到bootargs中,u-boot启动后会将bootargs中的信息传送给kernel,,kernel在启动的时候会解析bootargs中mtdparts的部分,这边举个例子:
mtdparts=nand.0:1M(Bootloader)ro,31M(Kernel)ro,16M(User),96M(File System),更具体的mtdparts格式可以查阅下相关资料。
为了使kernel能够解析mtdparts信息,我们需要将内核中的Device Drivers -> Memory Technology Device (MTD) support ->Command line partition table parsing选项开启,这在上面已经说过。
在内核中添加分区表的时候,我们是在平台设备里面加入mtd_partition信息。这边通过u-boot传参则取消平台设备里面的partition信息,那我们需要怎样解析u-boot的传过来的mtdparts呢。
u-boot传参过来后,cmdlinepart.c中会将这些参数解析好,存在里面LIST_HEAD(part_parsers)链表里面,然后我们在驱动的probe函数中,通过调用mtd_device_parse_register(mtd, probe_types,&ppdata, NULL, 0);函数。
mtd_device_parse_register()函数位于drivers/mtd/mtdcore.c 中,内容如下:
intmtd_device_parse_register(structmtd_info*mtd,constchar*const*types,
structmtd_part_parser_data*parser_data,
conststructmtd_partition*parts,
intnr_parts)
{
interr;
structmtd_partition*real_parts;
err=parse_mtd_partitions(mtd,types,&real_parts,parser_data);
if(err<=0&&nr_parts&&parts){
real_parts=kmemdup(parts,sizeof(*parts)*nr_parts,
GFP_KERNEL);
if(!real_parts)
err=-ENOMEM;
else
err=nr_parts;
}
if(err>0){
err=add_mtd_partitions(mtd,real_parts,err);
kfree(real_parts);
}elseif(err==0){
err=add_mtd_device(mtd);
if(err==1)
err=-ENODEV;
}
returnerr;
}可以看到该函数会先执行parse_mtd_partitions(mtd, types, &real_parts, parser_data);函数,后面还是通过add_mtd_partitions()函数来实现分区表的添加。
parse_mtd_partitions()函数位于drivers/mtd/mtdpart.c中,内容如下:
intparse_mtd_partitions(structmtd_info*master,constchar*const*types,
structmtd_partition**pparts,
structmtd_part_parser_data*data)
{
structmtd_part_parser*parser;
intret=0;
if(!types)
types=default_mtd_part_types;
for(;ret<=0&&*types;types++){
parser=get_partition_parser(*types);
if(!parser&&!request_module("%s",*types))
parser=get_partition_parser(*types);
if(!parser)
continue;
ret=(*parser->parse_fn)(master,pparts,data);
put_partition_parser(parser);
if(ret>0){
printk(KERN_NOTICE"%d%spartitionsfoundonMTDdevice%s\n",
ret,parser->name,master->name);
break;
}
}
returnret;
}进入parse_mtd_partitions()函数会先判断types的类型,如果为空则给默认值,types的类型一般就两种,如下:
staticconstchar*constdefault_mtd_part_types[]={
"cmdlinepart",
"ofpart",
NULL
};第一个"cmdlinepart"即u-boot传参的方式,第二个"ofpart"即下面要讲到的使用dts传参的方式,判断完类型后,就通过get_partition_parser去解析part_parsers链表里面的数据,这样就完成u-boot参数的解析。
3.dts传参
在Linux3.14以后的linux版本中,加入一个新的知识DTS(Device tree),dts其实就是为了解决ARM Linux中的冗余代码,在Linux2.6版本的arch/arm/plat.xxx和arch/arm/mach.xxx中充斥着大量的垃圾代码,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码,关于dts可以自行查阅资料。
dts传参的原理其实和u-boot一样,区别在于:u-boot的时候是通过cmdlinepart.c文件实现分区信息写入LIST_HEAD(part_parsers)链表,dts则是用过ofpart.c文件实现分区信息写入LIST_HEAD(part_parsers)链表,所以同样要把ofpart.c文件的宏打开,在调用mtd_device_parse_register(mtd, probe_types,&ppdata, NULL, 0);函数的时候types要设置成ofpart。
如果去对比Linux2.6版本和Linux3.14版本,会发现drivers/mtd/ofpart.c和drivers/mtd/mtdpart.c文件有所不同,Linux3.8版本里面多了Device tree这一部分的内容,感兴趣的可以自己深究下。
这边举个dts的例子:
pinctrl-0=<&s3c_nand_flash>;
ranges=<000x0000000000000x000008000000>;/*CS0:NAND*/
nand@0,0{
partition@1{
label="Bootloader";
reg=<0x0000000000000x000000100000>;
};
partition@2{
label="Kernel";
reg=<0x0000001000000x000002000000>;
};
partition@3{
label="User";
reg=<0x0000020000000x000003000000>;
};
partition@4{
label="FileSystem";
reg=<0x0000030000000x000008000000>;
};
};到此,相信大家对“linux mtd的概念是什么”有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
推荐内容:什么是Linux MTD设备文件系统

发表评论 取消回复