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 };