MTD(memory technology device 内存技术设备)是用于访问memory 设备(ROM、flash)的Linux的子系统。
MTD 的主要目的是为了使新的memory 设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。
MTD 的所有源代码在/drivers/mtd 子目录下。
概念:
CFI: Common Flash Interface,通用Flash 接口,Intel 发起的一个Flash 的接口标准
OOB: out of band,某些内存技术支持out-of-band 数据—--如NAND 每512字节的块有16 个字节的extra data,用于纠错或元数据。
ECC: error correction,某些硬件不仅允许对flash 的访问,也有ecc 功能,所有flash器件都受位交换现象的困扰。在某些情况下,一个比特位会发生反转或被报告反转了,就要采用ECC 算法。
erasesize: 一个 erase 命令可以擦除的最小块的尺寸
buswidth: MTD 设备的接口总线宽度
interleave: 交错数,几块芯片平行连接成一块,使 buswidth 变大!!!
devicetype: 芯片类型,x8、x16 或者x32
Wear out: Flash 的擦除次数有限制,一般在100!!!
mtd_info结构是MTD原始设备层的一个重要结构,该结构定义了大量的关于MTD的数据和操作,定义在include/linux/mtd/mtd.h头文件。mtd_info结构成员主要由数据成员和操作函数两部分组成。
struct mtd_part {
struct mtd_info mtd;
struct mtd_info *master;
uint64_t offset;
struct list_head list;
};
总结
两种注册方式:
1)直接注册整个flash设备(MTD Device)到MTD。
ret = add_mtd_device(mtd);
2)分partion添加到mtd_table,并将每个partion当成一个mtd设备注册到MTD。
if (!(partitions && num_part > 0) )
ret = add_mtd_partitions(mtd, parts, num_part);
----------------------------------------------------------------------------
mtd_read:
直接直接调用mtd_info 的read 函数,因此,字符设备接口跳过了patition 这一层。
当count>0 时{
裁减本次操作大小len 至min(MAX_KMALLOC_SIZE,count),
申请一块大小为MAX_KMALLOC_SIZE 的内核空间kbuf,
调用mtd_info->read 将MTD 设备中的数据读入kbuf,
将kbuf 中的数据拷贝到用户空间buf,
count 自减
释放kbuf
}
----------------------------------------------------------------------------
Mtd_write
mtd_write 直接直接调用mtd_info 的write 函数,因此,字符设备接口跳过了patition 这一层。
当count>0 时{
裁减本次操作大小len 至min(MAX_KMALLOC_SIZE,count),
申请一块大小为MAX_KMALLOC_SIZE 的内核空间kbuf,
将用户空间buf 中的数据拷贝到kbuf,
调用mtd_info->write 将kbuf 中的数据读入MTD 设备,
count 自减
释放kbuf
}
主要原理是将Flash 的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。块设备模拟驱动按照block 号和偏移量来定位文件,因此在Flash 上除了文件数据,基本没有额外的控制数据。
----------------------------------------------------------------------------
Linux内核在MTD的下层实现了通用的NAND 驱动( 主要通过drivers/mtd/nand/nand_base.c 文件实现),因此芯片级的NAND 驱动不再需要实现mtd_info中的read()、write()、read_oob()、write_oob()等成员函数,而主体转移到了nand_chip数据结构
----------------------------------------------------------------------------
1. 如果Flash要分区,
则定义mtd_partition数组,将实际电路板中Flash分区信息记录于其中。
2. 在模块加载时分配和nand_chip的内存,根据目标板NAND 控制器的特殊情况初始化nand_chip 中的hwcontrol()、dev_ready()、calculate_ecc()、correct_data()、read_byte()、write_byte()等成员函数(如果不赋值会使用nand_base.c中的默认函数),注意将mtd_info的priv置为nand_chip。
3. 以mtd_info为参数调用nand_scan()函数探测NAND Flash的存在。
4. 如果要分区,
则以mtd_info和mtd_partition为参数调用add_mtd_partitions()添加分区信息。
----------------------------------------------------------------------------
gpmi_nfc_probe
gpmi_nfc_mil_init
初始化nandchip
nand->cmd_ctrl = mil_cmd_ctrl;
nand->dev_ready = mil_dev_ready;
nand->select_chip = mil_select_chip;
nand->read_byte = mil_read_byte;
nand->read_buf = mil_read_buf;
nand->write_buf = mil_write_buf;
……
nand_scan探测
mil_partitions_init
add_mtd_device/add_mtd_partitions
----------------------------------------------------------------------------
754 if (mtd_has_cmdlinepart()) {
755 static const char *probes[] __initconst = {
756 "cmdlinepart", NULL
757 };
758
759 mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
760 &mtd_parts, 0); //-->aa
761 }
762
763 if (mtd_parts_nb <= 0) {
764 mtd_parts = pdata->parts;
765 mtd_parts_nb = pdata->nr_parts;
766 }
767
768 /* Register any partitions */
769 if (mtd_parts_nb > 0) {
770 ret = mtd_device_register(&info->mtd, mtd_parts,
771 mtd_parts_nb);
772 if (ret == 0)
773 info->partitioned = true;
774 }
-->aa
715 int parse_mtd_partitions(struct mtd_info *master, const char **types,
716 struct mtd_partition **pparts, unsigned long origin)
717 {
718 struct mtd_part_parser *parser;
719 int ret = 0;
720
721 for ( ; ret <= 0 && *types; types++) {
722 parser = get_partition_parser(*types);
723 if (!parser && !request_module("%s", *types))
724 parser = get_partition_parser(*types); //-->bb
725 if (!parser)
726 continue;
727 ret = (*parser->parse_fn)(master, pparts, origin);
728 if (ret > 0) {
729 printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
730 ret, parser->name, master->name);
731 }
732 put_partition_parser(parser);
733 }
734 return ret;
735 }
-->bb
获得该名字的分析器
679 static struct mtd_part_parser *get_partition_parser(const char *name) //"cmdlinepart"
680 {
681 struct mtd_part_parser *p, *ret = NULL;
682
683 spin_lock(&part_parser_lock);
684
685 list_for_each_entry(p, &part_parsers, list) //在链表上找到匹配的
686 if (!strcmp(p->name, name) && try_module_get(p->owner)) {
687 ret = p;
688 break;
689 }
690
691 spin_unlock(&part_parser_lock);
692
693 return ret;
694 }
-- drivers/mtd/cmdlinepart.c --
380 static struct mtd_part_parser cmdline_parser = {
381 .owner = THIS_MODULE,
382 .parse_fn = parse_cmdline_partitions,
383 .name = "cmdlinepart",
384 };
MTD 的主要目的是为了使新的memory 设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。
MTD 的所有源代码在/drivers/mtd 子目录下。
概念:
CFI: Common Flash Interface,通用Flash 接口,Intel 发起的一个Flash 的接口标准
OOB: out of band,某些内存技术支持out-of-band 数据—--如NAND 每512字节的块有16 个字节的extra data,用于纠错或元数据。
ECC: error correction,某些硬件不仅允许对flash 的访问,也有ecc 功能,所有flash器件都受位交换现象的困扰。在某些情况下,一个比特位会发生反转或被报告反转了,就要采用ECC 算法。
erasesize: 一个 erase 命令可以擦除的最小块的尺寸
buswidth: MTD 设备的接口总线宽度
interleave: 交错数,几块芯片平行连接成一块,使 buswidth 变大!!!
devicetype: 芯片类型,x8、x16 或者x32
Wear out: Flash 的擦除次数有限制,一般在100!!!
mtd_info结构是MTD原始设备层的一个重要结构,该结构定义了大量的关于MTD的数据和操作,定义在include/linux/mtd/mtd.h头文件。mtd_info结构成员主要由数据成员和操作函数两部分组成。
struct mtd_part {
struct mtd_info mtd;
struct mtd_info *master;
uint64_t offset;
struct list_head list;
};
总结
两种注册方式:
1)直接注册整个flash设备(MTD Device)到MTD。
ret = add_mtd_device(mtd);
2)分partion添加到mtd_table,并将每个partion当成一个mtd设备注册到MTD。
if (!(partitions && num_part > 0) )
ret = add_mtd_partitions(mtd, parts, num_part);
----------------------------------------------------------------------------
mtd_read:
直接直接调用mtd_info 的read 函数,因此,字符设备接口跳过了patition 这一层。
当count>0 时{
裁减本次操作大小len 至min(MAX_KMALLOC_SIZE,count),
申请一块大小为MAX_KMALLOC_SIZE 的内核空间kbuf,
调用mtd_info->read 将MTD 设备中的数据读入kbuf,
将kbuf 中的数据拷贝到用户空间buf,
count 自减
释放kbuf
}
----------------------------------------------------------------------------
Mtd_write
mtd_write 直接直接调用mtd_info 的write 函数,因此,字符设备接口跳过了patition 这一层。
当count>0 时{
裁减本次操作大小len 至min(MAX_KMALLOC_SIZE,count),
申请一块大小为MAX_KMALLOC_SIZE 的内核空间kbuf,
将用户空间buf 中的数据拷贝到kbuf,
调用mtd_info->write 将kbuf 中的数据读入MTD 设备,
count 自减
释放kbuf
}
主要原理是将Flash 的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。块设备模拟驱动按照block 号和偏移量来定位文件,因此在Flash 上除了文件数据,基本没有额外的控制数据。
----------------------------------------------------------------------------
Linux内核在MTD的下层实现了通用的NAND 驱动( 主要通过drivers/mtd/nand/nand_base.c 文件实现),因此芯片级的NAND 驱动不再需要实现mtd_info中的read()、write()、read_oob()、write_oob()等成员函数,而主体转移到了nand_chip数据结构
----------------------------------------------------------------------------
1. 如果Flash要分区,
则定义mtd_partition数组,将实际电路板中Flash分区信息记录于其中。
2. 在模块加载时分配和nand_chip的内存,根据目标板NAND 控制器的特殊情况初始化nand_chip 中的hwcontrol()、dev_ready()、calculate_ecc()、correct_data()、read_byte()、write_byte()等成员函数(如果不赋值会使用nand_base.c中的默认函数),注意将mtd_info的priv置为nand_chip。
3. 以mtd_info为参数调用nand_scan()函数探测NAND Flash的存在。
4. 如果要分区,
则以mtd_info和mtd_partition为参数调用add_mtd_partitions()添加分区信息。
----------------------------------------------------------------------------
gpmi_nfc_probe
gpmi_nfc_mil_init
初始化nandchip
nand->cmd_ctrl = mil_cmd_ctrl;
nand->dev_ready = mil_dev_ready;
nand->select_chip = mil_select_chip;
nand->read_byte = mil_read_byte;
nand->read_buf = mil_read_buf;
nand->write_buf = mil_write_buf;
……
nand_scan探测
mil_partitions_init
add_mtd_device/add_mtd_partitions
----------------------------------------------------------------------------
754 if (mtd_has_cmdlinepart()) {
755 static const char *probes[] __initconst = {
756 "cmdlinepart", NULL
757 };
758
759 mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
760 &mtd_parts, 0); //-->aa
761 }
762
763 if (mtd_parts_nb <= 0) {
764 mtd_parts = pdata->parts;
765 mtd_parts_nb = pdata->nr_parts;
766 }
767
768 /* Register any partitions */
769 if (mtd_parts_nb > 0) {
770 ret = mtd_device_register(&info->mtd, mtd_parts,
771 mtd_parts_nb);
772 if (ret == 0)
773 info->partitioned = true;
774 }
-->aa
715 int parse_mtd_partitions(struct mtd_info *master, const char **types,
716 struct mtd_partition **pparts, unsigned long origin)
717 {
718 struct mtd_part_parser *parser;
719 int ret = 0;
720
721 for ( ; ret <= 0 && *types; types++) {
722 parser = get_partition_parser(*types);
723 if (!parser && !request_module("%s", *types))
724 parser = get_partition_parser(*types); //-->bb
725 if (!parser)
726 continue;
727 ret = (*parser->parse_fn)(master, pparts, origin);
728 if (ret > 0) {
729 printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
730 ret, parser->name, master->name);
731 }
732 put_partition_parser(parser);
733 }
734 return ret;
735 }
-->bb
获得该名字的分析器
679 static struct mtd_part_parser *get_partition_parser(const char *name) //"cmdlinepart"
680 {
681 struct mtd_part_parser *p, *ret = NULL;
682
683 spin_lock(&part_parser_lock);
684
685 list_for_each_entry(p, &part_parsers, list) //在链表上找到匹配的
686 if (!strcmp(p->name, name) && try_module_get(p->owner)) {
687 ret = p;
688 break;
689 }
690
691 spin_unlock(&part_parser_lock);
692
693 return ret;
694 }
-- drivers/mtd/cmdlinepart.c --
380 static struct mtd_part_parser cmdline_parser = {
381 .owner = THIS_MODULE,
382 .parse_fn = parse_cmdline_partitions,
383 .name = "cmdlinepart",
384 };