内核版本:linux-4.9.37

linux的mtd概述

简介

        MTD(memory  technology  device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,

为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。将CFI接口的MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。

                                           mtd子系统_ide

一、Flash硬件驱动层

        硬件驱动层负责在init时驱动Flash硬件,Linux  MTD设备的NOR  Flash芯片驱动遵循CFI接口标准,其驱动程序位于drivers/mtd/chips子目录下。

NAND型Flash的驱动程序则位于/drivers/mtd/nand子目录下。

二、MTD原始设备

        原始设备层有两部分组成,一部分是MTD原始设备的通用代码,另一部分是各个特定的Flash的数据,例如分区

        用于描述  MTD原始设备的数据结构是  mtd_info,这其中定义了大量的关于  MTD的数据和操作函数。

        mtd_idr(mtdcore.c=>static  DEFINE_IDR(mtd_idr))是所有MTD原始设备的列表,所有mtd设备通过i  =  idr_alloc(&mtd_idr,  mtd,  0,  0,  GFP_KERNEL)等类似操作与mtd_idr建立连接。

        mtd_part(mtdpart.c)是用于表示MTD原始设备分区的结构,其中包含了mtd_info,因为每一个分区都是被看成一个MTD原始设备加在mtd_idr中的,

mtd_part.mtd中的大部分数据都从该分区的主分区mtd_part->master中获得(add_mtd_partitions)。

        在drivers/mtd/maps/子目录下存放的是特定的flash的数据,每一个文件都描述了一块板子上的flash。其中调用add_mtd_device()、del_mtd_device()建立/删除mtd_info结构

并将其加入/删除mtd_idr(或者调用add_mtd_partition()、del_mtd_partition()(mtdpart.c)建立/删除mtd_part结构并将mtd_part.mtd_info加入/删除mtd_idr  中)。

三、MTD设备层

        基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(主设备号90)。

        MTD字符设备的定义在mtdchar.c中实现,注册一系列file  operation函数(lseek、open、close、read、write)。

        MTD块设备的定义在mtdblock.c(其调用mtd_blkdevs.c的函数),在mtd_blkdevs.c=>  add_mtd_blktrans_dev函数中,

以mtd_blkdevs.c=>  block_device_operations  mtd_block_ops为ops,以mtdblock.c=>mtd_blktrans_ops  mtdblock_tr的.name和.major注册块设备、生成块设备节点。

四、设备节点

        通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和MTD块设备节点(主设备号为31),通过访问此设备节点即可访问MTD字符设备和块设备。

/dev/mtdN是字符设备节点,/dev/mtdblockN是块设备节点。

/dev/mtdN和/dev/mtdblockN的关系和区别​:

1.它们是对同一分区的不同的描述方法

2.生成节点的方法

        字符节点是mtdchar.c注册生成的,支持open,read,write,ioctl等。flash_erase,  nanddump等都是对字符节点,通过read,write,ioctl等执行。见分层调用流程

        块设备节点对应mtdblock.c和mtd_blkdevs.c,是Flash驱动中用add_mtd_partitions()添加MTD设备分区,而生成的对应的块设备。

        MTD块设备驱动程序可以让flash器件伪装成块设备,实际上它通过把整块的erase  block放到ram里面进行访问,然后再更新到flash,用户可以在这个块设备上创建通常的文件系统。

        对于MTD块设备,MTD设备层是不提供ioctl的实现方法的,不能使用nandwrite,flash_eraseall,flash_erase等工具去对/dev/mtdblockN去进行操作。

3.mtd-utils工具只能用与/dev/mtdN的MTD字符设备。mount、umount命令只对/dev/mtdblockN的MTD块设备有效。

五、根文件系统

        在Bootloader中将JFFS(或JFFS2)的文件系统映像jffs.image(或jffs2.img)烧到flash的某一个分区中,在/arch/arm/mach-your/arch.c文件的your_fixup函数中

将该分区作为根文件系统挂载。

六、文件系统

        内核启动后,通过mount  命令可以将flash中的其余分区作为文件系统挂载到mountpoint上。

mtd的字符设备和块设备的命名规则

Table 7-1. MTD /dev entries, corresponding MTD user modules, and relevant device major numbers


/dev entry



Accessible MTD user module



Device type



Major number



mtdN



char device



char



90



mtdrN



char device



char



90



mtdblockN



block device, read-only block device, JFFS, and JFFS2



block



31



nftlLN



NFTL



block



93



ftlLN



FTL



block



44


Table 7-2. MTD /dev entries, minor numbers, and naming schemes


/dev entry



Minor number range



Naming scheme



mtdN



0 to 32 per increments of 2



N = minor / 2



mtdrN



1 to 33 per increments of 2



N = (minor – 1) / 2



mtdblockN



0 to 16 per increments of 1



N = minor



nftlLN



0 to 255 per sets of 16



L = set;​​[2]​​ N = minor – (set – 1) x 16; N is not appended to entry name if its value is zero.



ftlLN



0 to 255 per sets of 16



Same as NFTL.


mtd工具

        mtd-utils可以编译出很多mtd相关工具,例如:mkfs,flash_erase,ubiformat,nanddump,mtdinfo

        下载地址:   ​​mtd官网​​       ​​mtd-utils下载地址​​         ​​mtd-utils版本差异​


分层注册函数

mtd子系统_块设备_02

mtd_blkdevs.c的struct mtd_notifier blktrans_notifier的list成员  会添加到  mtdcore.c的            链表头mtd_nitifier

mtdblock.c的struct mtd_blktrans_ops mtdblock_tr的list成员     会添加到  mtd_blkdevs.c的    链表头blktrans_majors

1.add_mtd_partations()和add_mtd_devices()区别:

add_mtd_partations():

        有多个(即定义了struct  partition_info)    时使用此函数。

        此函数先调用allocate_partition按照分区信息进行分区,再调用add_mtd_devices()

add_mtd_devices():

只有一个分区时使用。

注:

可以直接使用mtd_device_register()

第一个参数是主分区(描述整个设备)

第二个参数是struct  partition_info的parts首地址

第三个参数是struct  partition_info的parts_num成员

2.注册函数区别

调用关系如下,详细见代码流程

mtd_device_register

        add_mtd_partations

                add_mtd_devices

mtd子系统详细分层

若将mtd子系统详细分层,则如下图所示。

mtd子系统_mtd_03

字符节点分层调用流程

示例:nanddump  -p  -c  -l  0x800  /dev/mtd8

open

open(“/dev/mtd8”...)  =  3

        mtdchar_open(struct  inode  *inode,  struct  file  *file)                //mtdchar.c

                int  minor  =  iminor(inode);

                int  devnum  =  minor  >>  1;

                struct  mtd_info  *mtd;        

                struct  mtd_file_info  *mfi;

                mtd  =  get_mtd_device(NULL,  devnum);

                        struct  mtd_info  *ret  =  NULL

                        ret  =  idr_find(&mtd_idr,  num);

                        __get_mtd_device(ret);

                        return  ret;

                mfi  =  kzalloc(sizeof(*mfi),  GFP_KERNEL);

                mfi->mtd  =  mtd;

                file->private_data  =  mfi;

ioctl

ioctl(3,  MIXER_READ(18)  or  ECCGETSTATS,  ...)

        struct  mtd_file_info  *mfi  =  file->private_data;

        struct  mtd_info  *mtd  =  mfi->mtd;

        检查坏块:MEMSETBADBLOCK

        擦除:MEMERASE、MEMERASE64            

擦除

mtd_erase(mtd,  erase);                                                                   //mtdcore.c

        mtd->_erase(mtd,  instr)                                                           //nand_base.c=>nand_scan_tail中指定的

                nand_erase(mtd,  instr);                                                    //nand_base.c

                        nand_erase_nand(mtd,  instr,  0)                             //nand_base.c

                                chip->select_chip(mtd,  chipnr);                       //控制器驱动或nand_base.c=>nand_scan_ident中指定

                                nand_block_checkbad(mtd,  ((loff_t)  page)  <<  chip->page_shift,  allowbbt)   //若是坏块,则不擦除

                                        if (!chip->bbt)   //否则 return nand_isbad_bbt(mtd, ofs, allowbbt);   nand_bbt.c

                                              chip->block_bad(mtd, ofs);   //nand_base.c=> nand_scan_ident=> nand_set_defaults

                                                      nand_block_bad(mtd, ofs)      //nand_base.c

                                                              如果(chip->bbt_options & NAND_BBT_SCANLASTPAGE)

                                                                      ofs += mtd->erasesize - mtd->writesize;

                                                              chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);

                                                              bad = chip->read_byte(mtd);

                                                              res = bad != 0xFF;

                                                              如果(chip->bbt_options & NAND_BBT_SCAN2NDPAGE)

                                                                      会再读下一页的bb数据

                                                              return res;

                                chip->erase(mtd,  page  &  chip->pagemask)      //nand_base.c=>  nand_get_flash_type中指定

                                        single_erase(mtd,  page  &  chip->pagemask)

                                                //控制器驱动或nand_base.c=>nand_scan_ident中指定

                                                chip->cmdfunc(mtd,  NAND_CMD_ERASE1,  -1,  page);                

                                                       发送页地址

                                                chip->cmdfunc(mtd,  NAND_CMD_ERASE2,  -1,  -1);

read(3,  ...)  //应用层

mtdchar_read(struct  file  *file,  char  __user  *buf,  size_t  count,  loff_t  *ppos)    //mtdchar.c    

       //一般走switch的默认路径

        struct mtd_file_info *mfi = file->private_data;

        struct mtd_info *mtd = mfi->mtd;

        ret = mtd_read(mtd,  *ppos,  len,  &retlen,  kbuf);                           //mtdcore.c

                ret_code = mtd->_read(mtd,  from,  len,  retlen,  buf);           //mtdpart.c=> allocate_partition指定  

                           part_read(mtd,  from,  len,  retlen,  buf);           //mtdpart.c

                                         stats = part->master->ecc_stats;

                                         res = part->master->_read(part->master, from + part->offset, len, retlen, buf);

                                                  //nand_base.c=>nand_scan_tail指定

                                                  nand_read(mtd,  from,  len,  retlen,  buf)          //nand_base.c

                                                          ops.mode  =  MTD_OPS_PLACE_OOB;

                                                          ret = nand_do_read_ops(mtd,  from,  &ops);        //nand_base.c

                                                                uint8_t *oob; 

                                                                unsigned int max_bitflips = 0;

                                                                oob = ops->oobbuf;             

                                                                 ecc_failures = mtd->ecc_stats.failed;

                                                                 chip->select_chip(mtd,  chipnr);       

                                                                         //​控制器驱动​或nand_base.c=>nand_scan_ident => nand_set_defaults

                                                                 chip->cmdfunc(mtd,  NAND_CMD_READ0,  0x00,  page);                              

                                                                         //​控制器驱动​或nand_base.c=>nand_scan_ident => nand_set_defaults

                                                                         ​发送地址、读命令       ​                                        

                                                                 如果ops->mode是MTD_OPS_RAW

                                                                          ret = chip->ecc.read_page_raw(mtd,  chip,  bufpoi,  

                                                                                                                                     oob_required,  page);

                                                                           //nand_base.c=>nand_scan_tail函数中设置

                                                                  若没对齐 且 chip->options有NAND_SUBPAGE_READ 且

                                                                                                                                         ops->oobbuf是NULL

                                                                          ret = chip->ecc.read_subpage(mtd,  chip,  col,  

                                                                                                                         bytes,  bufpoi,  page);

                                                                  其他  //一般走此分支

                                                                          ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page);

                                                                   ret = chip->ecc.read_page(mtd,  chip,  bufpoi,  oob_required,  page);

                                                                           //nand_base.c=> nand_scan_tail指定

                                                                           nand_read_page_raw(mtd,  chip,  bufpoi,  oob_required,  page)

                                                                                   //nand_base.c       //​此函数返回值一定为0

                                                                                   chip->read_buf(mtd,  buf,  mtd->writesize);                                              

                                                                                          //​控制器驱动​或nand_base.c=>nand_scan_ident =>

                                                                                           //nand_set_defaults指定

                                                                                          ​只是把host->buf数据拷贝到buf

                                                                                          hisi_fmc_read_buf(struct mtd_info *mtd,

                                                                                                                                 uint8_t *buf, int len)

                                                                                                  如果读失败了:mtd->ecc_stats.failed++;

                                                                                                  设置ECC纠正的位数:mtd->ecc_stats.corrected += ..

                                                                       max_bitflips = max_t(unsigned int, max_bitflips, ret);

                                                                       if(oob)

                                                                                    oob = nand_transfer_oob(mtd, oob, ops, toread);

                                                                       //如果ecc出错了,重传

                                                                       if (mtd->ecc_stats.failed - ecc_failures) 

                                                                                //如果还有剩余尝试次数,重传

                                                                                if (retry_mode + 1 < chip->read_retries){

                                                                                                mtd->ecc_stats.failed = ecc_failures;

                                                                                                goto read_retry;

                                                                                }else{

                                                                                                ecc_fail = true;

                                                                                }

                                                                       }


                                                                       if (ret < 0)

                                                                                       return ret;

                                                                       if (ecc_fail)

                                                                                       return -EBADMSG;

                                                                       return max_bitflips; 

                                                               return ret;

                                        if ((mtd_is_eccerr(res)))

                                             mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;

                                       else

                                             mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; 

                                       return res;

                若ret_code < 0,直接返回

                若mtd->ecc_strength == 0,直接返回0

                若ret_code大于mtd->bitflip_threshold

                        返回-EUCLEAN

                否则

                        返回0

        如果读出错(ret != 0)

                ret = mtd_is_bitflip_or_eccerr(ret)             //mtd.h

                        return mtd_is_bitflip(err) || mtd_is_eccerr(err)

                              如果err是-EUCLEAN,是位反转(但被校正过来了);如果err是-EBADMSG,是ECC错误

                如果ret == 0或者错误类型是位反转或ecc错误,拷贝数据给用户        

write(3, ...)        //应用层

mtdchar_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)                //mtdchar.c

        //默认走default流程

        mtd_write(mtd, *ppos, len, &retlen, kbuf);               //mtdcore.c

                mtd->_write(mtd, to, len, retlen, buf);          //mtdpart.c=> allocate_partition指定 

                part_write(mtd, to, len, retlen, buf);           //mtdpart.c

                       part->master->_write(part->master, to + part->offset, len,  retlen, buf);

                       //nand_base.c=>nand_scan_tail指定

                       nand_write(mtd, to, len, retlen, buf);          //nand_base.c

                                ops.mode = MTD_OPS_PLACE_OOB;

                                nand_do_write_ops(mtd, to, &ops);                      //nand_base.c

                                        chip->select_chip(mtd, chipnr);          

                                        //​控制器驱动​或nand_base.c=>nand_scan_ident => nand_set_defaults中指定

                                        chip->write_page(mtd, chip, column, ..., (ops->mode == MTD_OPS_RAW))                  

                                        //控制器驱动或​nand_base.c=>nand_scan_tail​指定

                                               nand_write_page                                  //nand_base.c

                                                    chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);

                                                             设置写的地址

                                                    如果是raw,走chip->ecc.write_page_raw;

                                                    如果支持subpage,走chip->ecc.write_subpage;

                                                    其他:(一般走此分支)

                                                    chip->ecc.write_page(mtd, chip, buf, oob_required, page);

                                                    //nand_base.c=>nand_scan_tail指定

                                                            nand_write_page_raw(mtd, chip, buf, oob_required, page);    //nand_base.c

                                                                    chip->write_buf(mtd, buf, mtd->writesize);

                                                                    //​控制器驱动​或nand_base.c=>nand_scan_ident => nand_set_defaults

                                                                   ​ 只是将数据复制到host->buffer

                                                    chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);    

                                                    //​控制器驱动​或nand_base.c=>nand_scan_ident => nand_set_defaults指定 

                                                              ​执行烧写命令

控制器驱动probe流程

代码流程

hifmc100_nand_os.c        (drivers/mtd/nand/hifmc100_nand)

        hisi_nand_os_probe(struct  platform_device  *pltdev)

        struct  hik_nand_host  *host

        struct  nand_chip  *nand_chip;

        struct  mtd_info  *mtd;        

        struct  device  *dev  =  &pdev->dev;

        struct  device_node  *np  =  dev->of_node;

        dma_addr_t  dma_handle;

        host  =  devm_kzalloc(dev,  sizeof(*host),  GFP_KERNEL);

        pdev->dev.driver_data  =  host

        nand_chip  =  &host->chip;

        mtd    =  nand_to_mtd(nand_chip);                                        //即:mtd  =  &(nand_chip->mtd)

        host->mtd  =  mtd;

        nand_set_controller_data(nand_chip,  host);                        //nand_chip->priv  =  host

        nand_set_flash_node(nand_chip,  np);                                  //nand_chip->mtd.dev.of_node  =  pdev->dev.of_node

        host->buffer  =  dma_alloc_coherent(dev,  HIKFMC_BUFFER_LEN,  &dma_handle,  GFP_KERNEL)

        host->phy_buffer  =  dma_handle;

        设置nand_chip的函数及变量

                //读写选中芯片等函数,ecc模式等

                nand_chip->cmdfunc        =  hik_fmc_cmdfunc;

                nand_chip->select_chip        =  hik_fmc_select_chip;

                nand_chip->read_byte        =  hik_fmc_read_byte;

                nand_chip->read_buf        =  hik_fmc_read_buf;

                ...

                //nand_chip->options在nand.h中有所有宏

                nand_chip->options  =  NAND_SKIP_BBTSCAN  |  NAND_BROKEN_XD  |  NAND_SCAN_SILENT_NODEV;

                nand_chip->ecc.mode  =  NAND_ECC_NONE;

        //一般情况下,会调用nans_scan函数,nand_scan会调用nand_scan_ident和nand_scan_tail

        nand_scan_ident(mtd,  1,  NULL);                                            //nand_base.c

                //设置默认的读写等函数

                nand_set_defaults(chip,  chip->options  &  NAND_BUSWIDTH_16);

                        如果没设置chip的函数,则设置默认函数,对默认函数的设置如下。

                      (默认函数是drivers/mtd/nand/nand_base.c中的)

                        chip->read_byte  =  busw  ?  nand_read_byte16  :  nand_read_byte;

                        chip->block_bad  =  nand_block_bad;

                        chip->write_buf  =  busw  ?  nand_write_buf16  :  nand_write_buf

                        ...

                        // 这几个函数是一定会用到的,若不使用默认函数,则需要自己指定:

                        // select_chip,    cmdfunc,    read_byte,    read_buf,  write_buf,    dev_ready  

                        // chip->erase是强制设为默认函数的,见下边nand_get_type函数

                //获得nand  flash基本信息赋值到struct  nand_flash_dev结构体成员

                type  =  nand_get_flash_type(mtd,  chip,  &nand_maf_id,    &nand_dev_id,  table);

                        chip->erase  =  single_erase;                //nand_base.c

                        chip->options  |=  type->options;

                        打印flash相关信息:Manufacturer  ID、Chip  ID、SLC或者MLC、块大小、页大小、OOB大小等

                        chip->badblockbits = 8;

                        chip->erase = single_erase;

        hisifmc_host_init(host);                            //初始化fpu,spi,fmc控制器

        hisifmc_chipinfo_init(host);                        //控制器端的ECC模式、page大小等设置

        nand_scan_tail(mtd);                                 //nand_base.c

                struct  nand_chip  *chip  =  mtd->priv;

                struct nand_ecc_ctrl *ecc = &chip->ecc;

                分配并设置chip->buffer

                //设置内部oob buffer的位置(page data之后)

                chip->oob_poi = chip->buffers->databuf + mtd->writesize;

                mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);

                根据chip->ecc.mode设置chip->ecc的读写等函数成员、chip->ecc的size、bytes、strength成员

                如果mode为NAND_ECC_NONE,则强制设置上述成员

                        ecc->read_page  =  nand_read_page_raw;                //nand_base.c

                        ecc->write_page  =  nand_write_page_raw;

                        ecc->size  =  mtd->writesize;

                        ecc->bytes  =  0;

                        ecc->strength  =  0;

                        ...

                ecc->steps = mtd->writesize / ecc->size;

                ecc->total = ecc->steps * ecc->bytes;

                设置chip->subpagesize等

                设置mtd的读写等函数、type、ecc信息等

                        mtd->_erase  =  nand_erase;                                //nand_base.c

                        mtd->_read   =  nand_read;

                        mtd->_write  =  nand_write;

                        ...                

                创建坏块表

                mtd->priv    =  nand_chip;

        ptn_info  =  get_partition_info(host->mtd);      //获取分区信息

        if  (ptn_info)  {

                nr_parts  =  ptn_info->parts_num;

                parts  =  ptn_info->parts;

        }

        解析分区,然后注册mtd设备。找到一个分区就分配分区

        mtd_device_register(host->mtd,  parts,  nr_parts);       //include/linux/mtd/mtd.h

                //传进来第1个参数名为master,第2个为parts,第3个为nr_parts

                mtd_device_parse_register(master,  NULL,  NULL,  parts,  nr_parts)                     //mtdcore.c实现

                        //第一个参数是主分区(描述整个设备)

                        parse_mtd_partitions(mtd,  types,  &parsed,  parser_data)                             //mtdpart.c实现

                        mtd_add_device_partitions(mtd,  &parsed)                                                       //mtdcore.c实现

                                int  nbparts  =  parts->nr_parts;

                                if  (nbparts  ==  0  ||  IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))  {

                                        ret  =  add_mtd_device(mtd);

                                                        mtd->dev.type  =  &mtd_devtype;

                                                        mtd->dev.class  =  &mtd_class;

                                                        device_register(&mtd->dev);

                                                        device_create(&mtd_class,  mtd->dev.parent,  MTD_DEVT(i)  +  1,  

                                                                                      NULL,  "mtd%dro",  i);

                                }

                                if  (nbparts  >  0)  {

                                        ret  =  add_mtd_partitions(mtd,  real_parts,  nbparts);                     //mtdpart.c实现

                                                        struct  mtd_part  *slave;

                                                        printk("Creating  %d  MTD  partitions  on  \"%s\":\n",  nbparts,  master->name);

                                                        for  (i  =  0;  i  <  nbparts;  i++)  {

                                                                slave  =  allocate_partition(master,  parts  +  i,  i,  cur_offset);

                                                                                struct  mtd_part  *slave;

                                                                                slave  =  kzalloc(sizeof(*slave),  GFP_KERNEL);

                                                                                //设置slave的mtd_info成员mtd。mtd的变量成员基本都来自于master,

                                                                                // 函数成员基本都来自mtdpart.c

                                                                                slave->mtd.type  =  master->type;

                                                                                slave->mtd._read  =  part_read;

                                                                                slave->master  =  master;

                                                                                //打印本分区的区间和名称

                                                                                printk("0x%012llx-0x%012llx  :  \"%s\"\n",  

                                                                                      (unsigned  long  long)slave->offset,

                                                                                      (unsigned  long  long)(slave->offset  +  slave->mtd.size),  

                                                                                      slave->mtd.name);

                                                                                如果master->_block_isbad已经被设置

                                                                                uint64_t  offs  =  0;

                                                                                while  (offs  <  slave->mtd.size)  {

                                                                                //如果是被保留的块,则bbt

                                                                                if  (mtd_block_isreserved(master,  offs  +  slave->offset))

                                                                                        slave->mtd.ecc_stats.bbtblocks++;

                                                                                else  if  (mtd_block_isbad(master,  offs  +  slave->offset))

                                                                                        slave->mtd.ecc_stats.badblocks++;

                                                                                offs  +=  slave->mtd.erasesize;

                                                                list_add(&slave->list,  &mtd_partitions);

                                                                add_mtd_device(&slave->mtd);                                  //mtdcore.c

                                                                        i  =  idr_alloc(&mtd_idr,  mtd,  0,  0,  GFP_KERNEL)    

                                                                                //调用到的地方:  register_mtd_blktrans=>  mtd_for_each_device,

                                                                                //  其调用idr_get_next(&mtd_idr,  &i)

                                                                        mtd->index  =  i;


                                                                        mtd->dev.type  =  &mtd_devtype;

                                                                        mtd->dev.class  =  &mtd_class;

                                                                        mtd->dev.devt  =  MTD_DEVT(i);

                                                                        dev_set_name(&mtd->dev,  "mtd%d",  i);

                                                                        dev_set_drvdata(&mtd->dev,  mtd);

                                                                        //  在/dev下创建mtd%d节点。

                                                                        device_register(&mtd->dev);

                                                                        //  在/dev下创建mtd  %dro节点。

                                                                        device_create(&mtd_class,  mtd->dev.parent,  MTD_DEVT(i)  +  1,  NULL,

                                                                                                                "mtd%dro",  i);

                                                                        list_for_each_entry(not,  &mtd_notifiers,  list)

                                                                               not->add(mtd);                        //见核心层流程

                                        if  (ret  &&  IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))

                                                del_mtd_device(mtd);

                                        return  ret;

                                }

结构体关系图

probe函数确定的结构体关系图如下

mtd子系统_块设备_04

核心层流程

字符设备

mtdcore.c         (drivers/mtd)

        class_register(&mtd_class);

        mtd_bdi_init(&mtd_bdi, "mtd");                //mtdcore.c

        proc_mtd = proc_create("mtd", 0, NULL, &mtd_proc_ops);

        init_mtdchar();                                    //mtdchar.c (drivers/mtd)

                __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd", &mtd_fops);

                //mtdcore.c => add_mtd_device中会创建设备节点/dev/mtdN,对应mtd_fops (mtdchar.c定义)

块设备                

mtdblock.c  (drivers/mtd)    //此文件的init函数调用register_mtd_blktrans

static  struct  mtd_blktrans_ops  mtdblock_tr  =  {

        .name                =  "mtdblock",

        .major                =  MTD_BLOCK_MAJOR,    //MTD_BLOCK_MAJOR  =  31

        .part_bits        =  0,

        .blksize          =  512,

        ...

        .writesect        =  mtdblock_writesect,

        .add_mtd        =  mtdblock_add_mtd,

        .owner                =  THIS_MODULE,

//        struct  list_head  devs;

};

register_mtd_blktrans(&mtdblock_tr)                        //mtd_blkdevs.c实现,它定义static  LIST_HEAD(blktrans_majors);

        //如果是在注册第一个设备类型,则注册notifier。

        if  (!blktrans_notifier.list.next)                               //static  struct  mtd_notifier  blktrans_notifier  (mtd_blkdev.c)

                register_mtd_user(&blktrans_notifier);      //mtdcore.c

                        list_add(&blktrans_notifier->list,  &mtd_notifiers);     //static  LIST_HEAD(mtd_notifiers)          (mtdcore.c)

                        mtd_for_each_device(mtd)                     //此mtd与mtd_idr关联:static  DEFINE_IDR(mtd_idr);  //mtdcore.c

                                                                                           //add_mtd_device里边,把每个mtd设备关联到了mtd_idr

                                //对于mtd_idr上的每个设备(mtd),调用blktrans_notifier->add,会调用blktrans_majors链表头上每

                                 //个成员(tr)的add_mtd函数(以tr和mtd为参数)。

                                new->add(mtd);


        register_blkdev(mtdblock_tr->major,  mtdblock_tr->name)                //注册块设备

        list_add(&mtdblock_tr->list,  &blktrans_majors);                                //把本tr挂到blktrans_majors链表头上

        mtd_for_each_device(mtd)                                                                                                                                                                                

                if  (mtd->type  !=  MTD_ABSENT)

                        mtdblock_tr->add_mtd(mtdblock_tr,  mtd);

                                mtdblock_add_mtd

                                struct  gendisk  *gd;

                                struct  mtdblk_dev  *dev  =  kzalloc(sizeof(*dev),  GFP_KERNEL);

                                dev->mbd.mtd  =  mtd;

                                dev->mbd.devnum  =  mtd->index;

                                dev->mbd.size  =  mtd->size  >>  9;

                                dev->mbd.tr  =  mtdblock_tr;

                                add_mtd_blktrans_dev(&dev->mbd)                                //mtd_blkdevs.c

                                        //传进来的参数名字为new

                                        struct  mtd_blktrans_ops  *tr  =  new->tr;    //tr  =  new->tr  =  mtdblock_tr

                                        struct  mtd_blktrans_dev  *d;

                                        list_for_each_entry(d,  &tr->devs,  list)  {

                                                //第一次执行时,tr->devs上没有成员,直接跳出本循环

                                                //如果不是第一次执行

                                                        //如果d->devnum  ==  new->devnum,说明此号已经被使用,直接return  -EBUSY

                                                        //如果d->devnum  >  new->devnum,说明已经添加过了,直接跳出到added标号

                                                //如果执行到这里,说明d->devnum  <  new->devnum

                                                list_add_tail(&new->list,  &tr->devs);


                                                gd  =  alloc_disk(1  <<  tr->part_bits);


                                                new->disk  =  gd;

                                                        gd->private_data  =  new;

                                                        gd->major  =  tr->major;

                                                        gd->first_minor  =  (new->devnum)  <<  tr->part_bits;

                                                        gd->fops  =  &mtd_block_ops;

                                                //设置gd->dist_name为/dev/mtdblockN

                                                snprintf(gd->disk_name,  sizeof(gd->disk_name),  "%s%d",  tr->name,  new->devnum);                                                

                                                /*  Create  the  request  queue  */

                                                new->rq  =  blk_init_queue(mtd_blktrans_request,  &new->queue_lock);

                                                new->rq->queuedata  =  new;


                                                gd->queue  =  new->rq;


                                                new->wq  =  alloc_workqueue("%s%d",  0,  0,  tr->name,  new->mtd->index);

                                                device_add_disk(&new->mtd->dev,  gd);                //genhd.c  (drivers/block)

                                                        //传进来的第二个参数名为disk

                                                        disk->major  =  MAJOR(devt);

                                                        disk->first_minor  =  MINOR(devt);

                                                        register_disk(parent,  disk);        

                                                                struct  device  *ddev  =  disk_to_dev(disk);

                                                                dev_set_name(ddev,  "%s",  disk->disk_name);        

                                                                device_add(ddev)

/sys/class/mtd的相关成员

mtd的相关信息可以在/sys/class/mtd/mtdN下直接cat得到,对应相应分区的mtd_info结构体

dev:                              主设备号和次设备号

name:                              分区的名字。(自己定义的分区表(partition_info)的分区的名字)

type:                              设备类型。如:nand,nor。对应MTD_NORFLASH、MTD_NANDFLASH等(可参考mtd-abi.h)。

size:                              本分区总大小。10进制的

erasesize:                    擦除大小。10进制的,一般是一个块大小。

writesize:                    写大小。10进制的,一般是一个页大小。

subpagesize:                子页大小。10进制的。

oobsize:                        oob大小。10进制的。

numeraseregions:        可变擦除区域数量。如果是0,则表示整个设备的擦除大小都是erasesize

flags:                            MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h)

常用函数

nand.h中有一些常用函数:nand_set_flash_node、nand_get_flash_node、mtd_to_nand、nand_to_mtd、nand_get_controller_data、nand_set_controller_data

它们实现如下:

static  inline  void  nand_set_flash_node(struct  nand_chip  *chip,  struct  device_node  *np)

{

        mtd_set_of_node(&chip->mtd,  np);

}

static  inline  struct  device_node  *nand_get_flash_node(struct  nand_chip  *chip)

{

        return  mtd_get_of_node(&chip->mtd);

}

static  inline  struct  nand_chip  *mtd_to_nand(struct  mtd_info  *mtd)

{

        return  container_of(mtd,  struct  nand_chip,  mtd);

}

static  inline  struct  mtd_info  *nand_to_mtd(struct  nand_chip  *chip)

{

        return  &chip->mtd;

}

static  inline  void  *nand_get_controller_data(struct  nand_chip  *chip)

{

        return  chip->priv;

}

static  inline  void  nand_set_controller_data(struct  nand_chip  *chip,  void  *priv)

{

        chip->priv  =  priv;

}