Linux MTD 源代码分析

 

 

 

 

 

by Jim Zeus

Version 0.1

2002/4/29

 

 

 


 

 

 

Copyright © 2002 Jim Zeus. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License,Version 1.1 or any later version published by the Free Software Foundation;with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

 

 

 

 

If you got any Problem, Suggestion, Advice or Question ,

Please mail to: jimzeus@sina.com.cn

Any correction will be appreciated.

 

 

 

 


目录

专有名词:.... 7

Linux MTD介绍:... 8

设备层和原始设备层的函数调用关系(红色部分需要我们实现):... 9

NORFlash芯片驱动与MTD原始设备... 10

NANDNOR的比较... 11

源码分析... 14

头文件分析... 14

mtd.h. 14

MTD_CHAR_MAJOR. 14

MTD_BLOCK_MAJOR. 14

MAX_MTD_DEVICES. 14

mtd_info. 14

mtd_info.type的取值... 16

mtd_info.flags的取值... 16

mtd_info.ecctype的取值... 17

erase_info. 17

erase_info.state的取值... 17

mtd_notifier 17

get_mtd_device. 18

put_mtd_device. 19

partitions.h. 19

mtd_partition. 19

MTDPART_OFS_APPEND.. 20

MTDPART_SIZ_FULL. 20

map.h. 20

map_info. 20

mtd_chip_driver 21

gen_probe.h. 21

chip_probe. 22

cfi.h. 22

cfi_private. 22

cfi_private.cfi_mode的取值... 22

cfi_ident 23

cfi_ident.P_ID的取值... 23

flashchip.h. 24

flchip. 24

/drivers/mtd/子目录... 24

mtdcore.c. 25

mtd_table. 25

mtd_notifiers. 25

add_mtd_device. 25

del_mtd_device. 26

register_mtd_user 28

unregister_mtd_user 29

__get_mtd_device. 30

mtdpart.c. 31

mtd_partitions. 32

mtd_part 32

PART(x) 32

add_mtd_partitions. 32

del_mtd_partitions. 36

part_read. 37

part_write. 37

part_readv. 37

part_writev. 37

part_erase. 37

part_lock. 37

part_unlock. 37

part_sync. 37

part_suspend. 37

part_resume. 37

mtdblock.c. 38

notifier 38

mtdblk_dev. 38

mtdblks. 39

erase_callback. 39

erase_write. 39

write_cached_data. 41

do_cached_write. 42

do_cached_read. 45

mtdblock_open. 46

mtdblock_release. 49

handle_mtdblock_request 50

leaving. 52

mtdblock_thread. 52

mtdblock_ioctl 54

mtd_fops. 55

init_mtdblock. 55

cleanup_mtdblock. 57

mtdchar.c. 58

notifier 58

mtd_lseek. 58

mtd_open. 59

mtd_close. 61

MAX_KMALLOC_SIZE. 61

mtd_read. 62

mtd_write. 63

mtd_erase_callback. 65

mtd_ioctl 66

mtd_fops. 71

init_mtdchar 72

cleanup_mtdchar 73

/drivers/mtd/chips子目录... 74

chipreg.c. 74

chip_drvs_list 74

register_mtd_chip_driver 74

unregister_mtd_chip_driver 75

get_mtd_chip_driver 76

do_map_probe. 77

cfi_probe.c. 78

cfi_chipdrv. 78

cfi_probe_init 78

cfi_probe_exit 79

cfi_probe. 80

cfi_chip_probe. 80

cfi_probe_chip. 80

qry_present 81

cfi_chip_setup. 82

jedec_probe.c. 84

amd_flash_info. 84

jedec_table. 84

jedec_chipdrv. 85

jedec_probe_init 85

jedec_probe_exit 85

jedec_probe. 86

jedec_probe_chip. 87

jedec_probe_chip. 87

cfi_jedec_setup. 87

gen_probe.c. 88

mtd_do_chip_probe. 88

genprobe_ident_chips. 89

genprobe_new_chip. 90

check_cmd_set 91

cfi_cmdset_unkown. 92

cfi_cmdset_0002.c. 93

cfi_amdstd_chipdrv. 93

cfi_cmdset_0002. 93

cfi_amdstd_setup. 96

cfi_amdstd_erase_onesize. 99

do_erase_oneblock. 101

cfi_amdstd_read. 103

do_read_onechip. 105

cfi_amdstd_write. 106

do_write_oneword. 111

cfi_amdstd_sync. 113

cfi_amdstd_suspend. 113

cfi_amdstd_resume. 113

cfi_amdstd_destroy. 113

cfi_amdstd_erase_varsize. 113

cfi_amdstd_init 114

cfi_amdstd_exit 114

cfi_cmdset_0001.c. 115

/drivers/mtd/maps子目录... 115

your-flash.c. 115

WINDOW_ADDR. 115

WINDOW_SIZE. 115

BUSWIDTH. 115

mymtd. 116

your_read8. 116

your_read16. 117

your_read32. 117

your_copy_from.. 117

your_write8. 117

your_write16. 117

your_write32. 117

your_copy_to. 117

your_set_vpp. 117

your_partition. 118

NUM_PARTITIONS. 119

your_map. 119

init_yourflash. 119

cleanup_yourflash. 120

 

 

 

 


专有名词:

 

1. MTDMemory Technology Device,内存技术设备,

2. JEDECJoint Electron Device Engineering Council,电子电器设备联合会

3. CFICommon Flash Interface,通用Flash接口,Intel发起的一个Flash的接口标准

4. OOB out of band,某些内存技术支持out-of-band数据——例如,NAND flash512字节的块有16个字节的extra data,用于纠错或元数据。

5. ECC error correction,某些硬件不仅允许对flash的访问,也有ecc功能,所有flash器件都受位交换现象的困扰。在某些情况下,一个比特位会发生反转或被报告反转了,如果此位真的反转了,就要采用ECC算法。

6. erasesize 一个erase命令可以擦除的最小块的尺寸

7. buswidthMTD设备的接口总线宽度

8. interleave交错数,几块芯片平行连接成一块芯片,使buswidth变大

9. devicetype芯片类型,x8x16或者x32

10.              NAND一种Flash技术,参看NANDNOR的比较

11.              NOR一种Flash技术,参看NANDNOR的比较

 

 

 

 

 

 

 

 


Linux MTD介绍:

MTD(memory technology device内存技术设备)是用于访问memory设备(ROMflash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。我将CFI接口的MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


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

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

       用于描述MTD原始设备的数据结构是mtd_info,这其中定义了大量的关于MTD的数据和操作函数。mtd_tablemtdcore.c)则是所有MTD原始设备的列表,mtd_partmtd_part.c)是用于表示MTD原始设备分区的结构,其中包含了mtd_info,因为每一个分区都是被看成一个MTD原始设备加在mtd_table中的,mtd_part.mtd_info中的大部分数据都从该分区的主分区mtd_part->master中获得。

       drivers/mtd/maps/子目录下存放的是特定的flash的数据,每一个文件都描述了一块板子上的flash。其中调用add_mtd_device()del_mtd_device()建立/删除mtd_info结构并将其加入/删除mtd_table(或者调用add_mtd_partition()del_mtd_partition()mtdpart.c)建立/删除mtd_part结构并将mtd_part.mtd_info加入/删除mtd_table 中)。

三、MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。MTD字符设备的定义在mtdchar.c中实现,通过注册一系列file operation函数(lseekopenclosereadwrite)。MTD块设备则是定义了一个描述MTD块设备的结构mtdblk_dev,并声明了一个名为mtdblks的指针数组,这数组中的每一个mtdblk_devmtd_table中的每一个mtd_info一一对应。

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

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

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

 

设备层和原始设备层的函数调用关系(红色部分需要我们实现):

一个MTD原始设备可以通过mtd_part分割成数个MTD原始设备注册进mtd_tablemtd_table中的每个MTD原始设备都可以被注册成一个MTD设备,其中字符设备的主设备号为90,次设备号为0246…(奇数次设备号为只读设备),块设备的主设备号为31,次设备号为0123…

 

 


设备层

 

 


                            register_mtd_user()                    

get_mtd_device()         

                            unregister_mtd_user()          

put_mtd_device()

erase_info

mtd_notifiers

mtd_table

mtd_info

mtd_part

mtdcore.c

mtdpart.c

 

 

Your Flash

 

your-flash.c

 
 


                                                                       add_mtd_partitions()

                                                                       del_mtd_partitions()

原始设备层                                                     add_mtd_device()

                                                                       del_mtd_device()

                                                                       mtd_partition

 

 

 


NORFlash芯片驱动与MTD原始设备

       所有的NORFlash的驱动(探测probe)程序都放在drivers/mtd/chips下,一个MTD原始设备可以由一块或者数块相同的Flash芯片组成。假设由4devicetypex8Flash,每块大小为8Minterleave2,起始地址为0x01000000,地址相连,则构成一个MTD原始设备(0x01000000-0x03000000),其中两块interleave成一个chip,其地址从0x010000000x02000000,另两块interleave成一个chip,其地址从0x020000000x03000000

       请注意,所有组成一个MTD原始设备的Flash芯片必须是同类型的(无论是interleave还是地址相连),在描述MTD原始设备的数据结构中也只是采用了同一个结构来描述组成它的Flash芯片。

 

 

 

Chip#1      Chip#2

 

 

 

Chip#3     Chip#4

 
           0x03000000

 

 

 

           0x02000000

 

 

 

                 0x01000000

 

每个MTD原始设备都有一个mtd_info结构,其中的priv指针指向一个map_info结构,map_info结构中的fldrv_priv指向一个cfi_private结构,cfi_private结构的cfiq指针指向一个cfi_ident结构,chips指针指向一个flchip结构的数组。其中mtd_infomap_infocfi_private结构用于描述MTD原始设备;因为组成MTD原始设备的NORFlash相同,cfi_ident结构用于描述Flash芯片的信息;而flchip结构用于描述每个Flash芯片的专有信息(比如说起始地址)

 

 

 


NANDNOR的比较

  NORNAND是现在市场上两种主要的非易失闪存技术。Intel1988年首先开发出NOR flash技术,彻底改变了原先由EPROMEEPROM一统天下的局面。紧接着,1989年,东芝公司发表了NAND flash结构,强调降低每比特的成本,更高的性能,并且象磁盘一样可以通过接口轻松升级。但是经过了十多年之后,仍然有相当多的硬件工程师分不清NORNAND闪存。
  相“flash存储器经常可以与相“NOR存储器互换使用。许多业内人士也搞不清楚NAND闪存技术相对于NOR技术的优越之处,因为大多数情况下闪存只是用来存储少量的代码,这时NOR闪存更适合一些。而NAND则是高数据存储密度的理想解决方案。
  NOR的特点是芯片内执行(XIP, eXecute In Place),这样应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。NOR的传输效率很高,在14MB的小容量时具有很高的成本效益,但是很低的写入和擦除速度大大影响了它的性能。
  NAND结构能提供极高的单元密度,可以达到高存储密度,并且写入和擦除的速度也很快。应用NAND的困难在于flash的管理和需要特殊的系统接口。

性能比较

  flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。NAND器件执行擦除操作是十分简单的,而NOR则要求在进行擦除前先要将目标块内所有的位都写为0
  由于擦除NOR器件时是以64128KB的块进行的,执行一个写入/擦除操作的时间为5s,与此相反,擦除NAND器件是以832KB的块进行的,执行相同的操作最多只需要4ms
  执行擦除时块尺寸的不同进一步拉大了NORNADN之间的性能差距,统计表明,对于给定的一套写入操作(尤其是更新小文件时),更多的擦除操作必须在基于NOR的单元中进行。这样,当选择存储解决方案时,设计师必须权衡以下的各项因素。
  ● NOR的读速度比NAND稍快一些。
  ● NAND的写入速度比NOR快很多。
  ● NAND4ms擦除速度远比NOR5s快。
  大多数写入操作需要先进行擦除操作。
  ● NAND的擦除单元更小,相应的擦除电路更少。


接口差别
  NOR flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地存取其内部的每一个字节。
  NAND器件使用复杂的I/O口来串行地存取数据,各个产品或厂商的方法可能各不相同。8个引脚用来传送控制、地址和数据信息。
  NAND读和写操作采用512字节的块,这一点有点像硬盘管理此类操作,很自然地,基于NAND的存储器就可以取代硬盘或其他块设备。


容量和成本
  NAND flash的单元尺寸几乎是NOR器件的一半,由于生产过程更为简单,NAND结构可以在给定的模具尺寸内提供更高的容量,也就相应地降低了价格。
  NOR flash占据了容量为116MB闪存市场的大部分,而NAND flash只是用在8128MB的产品当中,这也说明NOR主要应用在代码存储介质中,NAND适合于数据存储,NANDCompactFlashSecure DigitalPC CardsMMC存储卡市场上所占份额最大。


可靠性和耐用性
  采用flahs介质时一个需要重点考虑的问题是可靠性。对于需要扩展MTBF的系统来说,Flash是非常合适的存储方案。可以从寿命(耐用性)、位交换和坏块处理三个方面来比较NORNAND的可靠性。
  寿命(耐用性)
  在NAND闪存中每个块的最大擦写次数是一百万次,而NOR的擦写次数是十万次。NAND存储器除了具有101的块擦除周期优势,典型的NAND块尺寸要比NOR器件小8倍,每个NAND存储器块在给定的时间内的删除次数要少一些。
  位交换
  所有flash器件都受位交换现象的困扰。在某些情况下(很少见,NAND发生的次数要比NOR),一个比特位会发生反转或被报告反转了。
  一位的变化可能不很明显,但是如果发生在一个关键文件上,这个小小的故障可能导致系统停机。如果只是报告有问题,多读几次就可能解决了。
  当然,如果这个位真的改变了,就必须采用错误探测/错误更正(EDC/ECC)算法。位反转的问题更多见于NAND闪存,NAND的供应商建议使用NAND闪存的时候,同时使用EDC/ECC算法。
  这个问题对于用NAND存储多媒体信息时倒不是致命的。当然,如果用本地存储设备来存储操作系统、配置文件或其他敏感信息时,必须使用EDC/ECC系统以确保可靠性。
  坏块处理
  NAND器件中的坏块是随机分布的。以前也曾有过消除坏块的努力,但发现成品率太低,代价太高,根本不划算。
  NAND器件需要对介质进行初始化扫描以发现坏块,并将坏块标记为不可用。在已制成的器件中,如果通过可靠的方法不能进行这项处理,将导致高故障率。

易于使用
  可以非常直接地使用基于NOR的闪存,可以像其他存储器那样连接,并可以在上面直接运行代码。
  由于需要I/O接口,NAND要复杂得多。各种NAND器件的存取方法因厂家而异。
  在使用NAND器件时,必须先写入驱动程序,才能继续执行其他操作。向NAND器件写入信息需要相当的技巧,因为设计师绝不能向坏块写入,这就意味着在NAND器件上自始至终都必须进行虚拟映射。


软件支持
  当讨论软件支持的时候,应该区别基本的读//擦操作和高一级的用于磁盘仿真和闪存管理算法的软件,包括性能优化。
  在NOR器件上运行代码不需要任何的软件支持,在NAND器件上进行同样操作时,通常需要驱动程序,也就是内存技术驱动程序(MTD)NANDNOR器件在进行写入和擦除操作时都需要MTD
  使用NOR器件时所需要的MTD要相对少一些,许多厂商都提供用于NOR器件的更高级软件,这其中包括M-SystemTrueFFS驱动,该驱动被Wind River SystemMicrosoftQNX Software SystemSymbianIntel等厂商所采用。
  驱动还用于对DiskOnChip产品进行仿真和NAND闪存的管理,包括纠错、坏块处理和损耗平衡。

 

                                                                      ——From M-system公司Arie TAL

 

 

 

 


源码分析

下面具体介绍每个文件的内容和作用:

 

 

头文件分析

 

mtd.h

MTD_CHAR_MAJOR

#define MTD_CHAR_MAJOR 90         MTD字符设备的主设备号

MTD_BLOCK_MAJOR

#define MTD_BLOCK_MAJOR 31       MTD块设备的主设备号

MAX_MTD_DEVICES

#define MAX_MTD_DEVICES 16       最大MTD原始设备数

 

 

mtd_info

表示MTD原始设备的结构,每个分区也被实现为一个mtd_info,如果有两个MTD原始设备,每个上有三个分区,在系统中就一共有6mtd_info结构,这些mtd_info的指针被存放在名为mtd_table的数组里。

struct mtd_info {         

       u_char type;          内存技术的类型

       u_int32_t flags;      标志位

       u_int32_t size; // Total size of the MTD             mtd设备的大小

 

       /* "Major" erase size for the device. Nae users may take this

        * to be the only erase size available, or may use the more detailed

        * information below if they desire

        */

       u_int32_t erasesize;“主要的”erasesize(同一个mtd设备可能有数种不同的erasesize

 

       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)             oob块大小

       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)              oob特别数据大小

       u_int32_t ecctype;         ecc类型

       u_int32_t eccsize;          自动ecc可以工作的范围

 

       // Kernel-only stuff starts here.

       char *name;

       int index;

 

       /* Data for variable erase regions. If numeraseregions is zero,

        * it means that the whole device has erasesize as given above.

        */不同的erasesize的区域

       int numeraseregions; 不同erasesize的区域的数目(通常是1

       struct mtd_erase_region_info *eraseregions;

 

       /* This really shouldn't be here. It can go away in 2.5 */

       u_int32_t bank_size;

 

       struct module *module;

       int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

                     routine用于将一个erase_info加入erase queue

       /* This stuff for eXecute-In-Place */

       int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);

 

       /* We probably shouldn't allow XIP if the unpoint isn't a NULL */

       void (*unpoint) (struct mtd_info *mtd, u_char * addr);

 

 

       int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

       int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

       int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf);

       int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf);

 

       int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

       int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

       /* iovec-based read/write methods. We need these especially for NAND flash,

          with its limited number of write cycles per erase.

          NB: The 'count' parameter is the number of _vectors_, each of

          which contains an (ofs, len) tuple.

       */

       int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);

       int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);

 

       /* Sync */

       void (*sync) (struct mtd_info *mtd);

 

       /* Chip-supported device locking */

       int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);

       int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);

 

       /* Power Management functions */

       int (*suspend) (struct mtd_info *mtd);

       void (*resume) (struct mtd_info *mtd);

 

       void *priv;                    //指向map_info结构

}

 

mtd_info.type的取值

#define MTD_ABSENT        0

#define MTD_RAM                     1

#define MTD_ROM                     2

#define MTD_NORFLASH           3

#define MTD_NANDFLASH        4

#define MTD_PEROM          5

#define MTD_OTHER          14

#define MTD_UNKNOWN           15

 

mtd_info.flags的取值

#define MTD_CLEAR_BITS        1       // Bits can be cleared (flash)

#define MTD_SET_BITS             2       // Bits can be set

#define MTD_ERASEABLE          4       // Has an erase function

#define MTD_WRITEB_WRITEABLE 8       // Direct IO is possible

#define MTD_VOLATILE            16      // Set for RAMs

#define MTD_XIP                32    // eXecute-In-Place possible

#define MTD_OOB                     64    // Out-of-band data (NAND flash)

#define MTD_ECC               128  // Device capable of automatic ECC

 

// Some common devices / combinations of capabilities

#define MTD_CAP_ROM            0

#define MTD_CAP_RAM             (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)

#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)

#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)

#define MTD_WRITEABLE         (MTD_CLEAR_BITS|MTD_SET_BITS)

 

mtd_info.ecctype的取值

#define MTD_ECC_NONE           0     // No automatic ECC available

#define MTD_ECC_RS_DiskOnChip    1     // Automatic ECC on DiskOnChip

#define MTD_ECC_SW        2     // SW ECC for Toshiba & Samsung devices

 

erase_info

表示erase动作的结构,由设备层调用mtd_info->erase函数传递给原始设备层

struct erase_info {        

       struct mtd_info *mtd;    被操作的MTD原始设备

       u_int32_t addr;              被操作的地址(byte

       u_int32_t len;                长度

       u_long time;

       u_long retries;

       u_int dev;

       u_int cell;

       void (*callback) (struct erase_info *self);   

callback函数指针,当erase结束后该函数被调用

       u_long priv;                  user模块提供的private数据

       u_char state;                 当前状态

       struct erase_info *next;  erase队列中的下一个erase_info

};

 

erase_info.state的取值

#define MTD_ERASE_PENDING          0x01

#define MTD_ERASING                                   0x02

#define MTD_ERASE_SUSPEND               0x04

#define MTD_ERASE_DONE             0x08

#define MTD_ERASE_FAILED            0x10

驱动模块在erase要求进入erase 队列时将state设置为MTD_ERASE_PENDING,当callback被调用时设置为MTD_ERASE_DONEMTD_ERASE_FAILED

 

 

mtd_notifier

MTD通知器(这个名字很古怪,但我找不到更好的词来翻译),加入/删除MTD设备和原始设备时调用的函数,在设备层,当MTD字符设备或块设备注册时,如果定义了CONFIG_DEVFS_FS,则会将一个mtd_notifier加入MTD原始设备层的mtd_notifiers链表,其中的函数会在两种情况下被调用,一是加入/删除新的MTD字符/块设备时,此时调用该MTD字符/块设备的notifier对下层所有的MTD原始设备操作一遍,二是加入/删除新的MTD原始设备时,此时调用所有的notifier对该原始设备执行一遍。

struct mtd_notifier {                   

void (*add)(struct mtd_info *mtd);      加入时调用

void (*remove)(struct mtd_info *mtd); 删除时调用

       struct mtd_notifier *next;      指向mtd_notifiers队列中的下一个mtd_notifier

};

 

 

get_mtd_device

格式:

       static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)

注释:

      

功能:

       获得一个MTD设备

说明:

       调用__get_mtd_device()

参数:

       __get_mtd_device()

返回:

       要求的MTD原始设备

调用:

       __get_mtd_device()

被调用:

       设备层函数,在设备层的open函数中被调用以获得一个MTD原始设备

源代码:

{

       struct mtd_info *ret;

      

       ret = __get_mtd_device(mtd, num);

 

       if (ret && ret->module && !try_inc_mod_count(ret->module))

              return NULL;

 

       return ret;

}

 

put_mtd_device

格式:

       static inline void put_mtd_device(struct mtd_info *mtd)

注释:

      

功能:

       归还(put)一个使用完的MTD设备

说明:

      

参数:

       MTD设备

返回:

      

调用:

      

被调用:

       设备层函数,通常在设备层的release函数中调用。

源代码:

{

       if (mtd->module)

              __MOD_DEC_USE_COUNT(mtd->module);

}

 

 

partitions.h

此文件中的数据用来从/maps子目录下的文件向原始设备层传递分区信息

 

mtd_partition

/*

 * Partition definition structure:

 *

 * An array of struct partition is passed along with a MTD object to

 * add_mtd_partitions() to create them.

 *

 * For each partition, these fields are available:

 * name: string that will be used to label the partition's MTD device.

 * size: the partition size; if defined as MTDPART_SIZ_FULL, the partition

 *   will extend to the end of the master MTD device.

 * offset: absolute starting position within the master MTD device; if

 *   defined as MTDPART_OFS_APPEND, the partition will start where the

 *   previous one ended.

 * mask_flags: contains flags that have to be masked (removed) from the

 *   master MTD flag set for the corresponding MTD partition.

 *   For example, to force a read-only partition, simply adding

 *   MTD_WRITEABLE to the mask_flags will do the trick.

 *

 * Note: writeable partitions require their size and offset be

 * erasesize aligned.

 */

struct mtd_partition {    描述mtd设备分区的结构,在MTD原始设备层调用add_mtd_partions

传递分区信息

       char *name;           /* identifier string */     

       u_int32_t size;        /* partition size */

       u_int32_t offset;            /* offset within the master MTD space */

       u_int32_t mask_flags;    /* master MTD flags to mask out for this partition */将被掩去的标

志位,(例如如果在分区时置mask_flagsWRITEABLE,则此分区为只读分区)

};

 

 

MTDPART_OFS_APPEND

#define MTDPART_OFS_APPEND      (-1)         如果mtd_partition.offset取此值,表示此分区

offset紧接上一个分区的结束

MTDPART_SIZ_FULL

#define MTDPART_SIZ_FULL     (0)                 如果mtd_partion.size取此值,表示此分区一

直到MTD原始设备的最后结束

 

 

 

map.h

 

map_info

 

struct map_info {

       char *name;

       unsigned long size;

       int buswidth; /* in octets */

       __u8 (*read8)(struct map_info *, unsigned long);              Flash的读写函数,(NORFlash

       __u16 (*read16)(struct map_info *, unsigned long);   可以像内存一样进行读写),read8

       __u32 (*read32)(struct map_info *, unsigned long);  read16read32分别用于buswidth

124字节的情况(write同理)

       /* If it returned a 'long' I'd call it readl.

        * It doesn't.

        * I won't.

        * dwmw2 */

      

       void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);

       void (*write8)(struct map_info *, __u8, unsigned long);

       void (*write16)(struct map_info *, __u16, unsigned long);

       void (*write32)(struct map_info *, __u32, unsigned long);

       void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);

 

       void (*set_vpp)(struct map_info *, int);

       /* We put these two here rather than a single void *map_priv,

          because we want mappers to be able to have quickly-accessible

          cache for the 'currently-mapped page' without the _extra_

          redirection that would be necessary. If you need more than

          two longs, turn the second into a pointer. dwmw2 */

       unsigned long map_priv_1;

       unsigned long map_priv_2;

       void *fldrv_priv;           指向cfi_private结构

       struct mtd_chip_driver *fldrv;

};

 

 

mtd_chip_driver

struct mtd_chip_driver {

       struct mtd_info *(*probe)(struct map_info *map);     该芯片的probe程序

       void (*destroy)(struct mtd_info *);

       struct module *module;         模块

       char *name;           芯片名

       struct list_head list;

};

MTD芯片驱动器的结构

 

 

 

gen_probe.h

 

chip_probe

struct chip_probe {

       char *name;

       int (*probe_chip)(struct map_info *map, __u32 base,

                       struct flchip *chips, struct cfi_private *cfi);   

 

};

通用探测程序的参数结构,此结构类型将作为参数传进mtd_do_chip_probe()中,mtd_do_chip_probe()将间接调用其中的probe_chip函数

 

 

 

cfi.h

cfi_private

CFI私有信息

struct cfi_private {       

       __u16 cmdset;

       void *cmdset_priv;

       int interleave;         芯片交错数,interleave片芯片交织连接成一个芯片

       int device_type;      每一片Flash的类型(18bits216bits432bits

       int cfi_mode;         /* Are we a JEDEC device pretending to be CFI? */    CFI的模式

       int addr_unlock1;   命令地址,所谓“unlock”是指无论Flash处于读模式或写模式,都

       int addr_unlock2;          可向该地址写命令

       int fast_prog;         允许Fast Program模式

       struct mtd_info *(*cmdset_setup)(struct map_info *);

       struct cfi_ident *cfiq; /* For now only one. We insist that all devs     每个芯片的信息

                              must be of the same type. */                   (所有芯片是同类型的)

       int mfr, id;

       int numchips;         Flash芯片个数(N interleave算一块芯片)

       unsigned long chipshift; /* Because they're of the same type */

每块芯片大小*interleave=2chipshift次方

       const char *im_name;    /* inter_module name for cmdset_setup */

       struct flchip chips[0];  /* per-chip data structure for each chip */每一块芯片的信息

};

 

 

cfi_private.cfi_mode的取值

#define CFI_MODE_CFI      0            真的是CFI

#define CFI_MODE_JEDEC  1            JEDEC仿真CFI

 

cfi_ident

CFI接口芯片的查询结构,从芯片起始地址+0x10开始的0x2C个字节

/* Basic Query Structure */

struct cfi_ident {

  __u8  qry[3];           QRY       0x10,0x11,0x12

  __u16 P_ID;             制造商ID      Primary ID            0x13,0x14

  __u16 P_ADR;                               Primary Address    0x15,0x16

  __u16 A_ID;                                  Alternate ID          0x17,0x18     

  __u16 A_ADR;                               Alternate Address   0x19,0x1A

  __u8  VccMin;         逻辑供电Vcc最小电压       0x1B

  __u8  VccMax;        逻辑供电Vcc最大电压       0x1C

  __u8  VppMin;         编程/擦除供电Vpp最小电压      0x1D

  __u8  VppMax;        编程/擦除供电Vpp最大电压      0x1E

  __u8  WordWriteTimeoutTyp;         典型单字节/字写周期定时时间          0x1F

  __u8  BufWriteTimeoutTyp;                                              0x20

  __u8  BlockEraseTimeoutTyp; 典型单块擦除定时时间       0x21

  __u8  ChipEraseTimeoutTyp;   典型整片擦除定时时间       0x22

  __u8  WordWriteTimeoutMax; 单字节/字写周期最大定时时间         0x23

  __u8  BufWriteTimeoutMax;                                              0x24

  __u8  BlockEraseTimeoutMax; 单块擦除最大定时时间      0x25

  __u8  ChipEraseTimeoutMax; 整片擦除最大定时时间       0x26

  __u8  DevSize;                      器件体积                            0x27

  __u16 InterfaceDesc;               Flash器件接口识别码ID     0x28,0x29

  __u16 MaxBufWriteSize;                                                    0x2A,0x2B

  __u8  NumEraseRegions;       器件可擦除块区域个数       0x2C

  __u32 EraseRegionInfo[0]; /* Not host ordered */   擦除块区域信息:       0x2D0x3C

} __attribute__((packed));`           bit15-0=y该擦除块区域内含同样体积擦除块的个数=y+1

bit31-16=z该区域每个擦除块体积=z x 256字节

 

 

 

 

 

 

 

cfi_ident.P_ID的取值

#define P_ID_NONE 0

#define P_ID_INTEL_EXT 1

#define P_ID_AMD_STD 2

#define P_ID_INTEL_STD 3

#define P_ID_AMD_EXT 4

#define P_ID_MITSUBISHI_STD 256

#define P_ID_MITSUBISHI_EXT 257

#define P_ID_RESERVED 65535

 

 

flashchip.h

 

 

flchip

struct flchip {

       unsigned long start; /* Offset within the map */

       //     unsigned long len;          假设所有的Flash chip都是相同的,所以没有定义len

       /* We omit len for now, because when we group them together

          we insist that they're all of the same size, and the chip size

          is held in the next level up. If we get more versatile later,

          it'll make it a damn sight harder to find which chip we want from

          a given offset, and we'll want to add the per-chip length field

          back in.

       */

       flstate_t state;

       flstate_t oldstate;

       spinlock_t *mutex;

       spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */

       wait_queue_head_t wq; /* Wait on here when we're waiting for the chip

                          to be ready */

       int word_write_time;

       int buffer_write_time;

       int erase_time;

};

 

 

 

/drivers/mtd/子目录

 

 

mtdcore.c

本文件主要实现了MTD原始设备层的数据结构mtd_table和对其的操作函数,主要包括add_mtd_device()(加入MTD原始设备)、del_mtd_device()(删除MTD原始设备)

mtd_table

static struct mtd_info *mtd_table[MAX_MTD_DEVICES];

标示MTD设备的数组,最多可以有MAX_MTD_DEVICES个设备(每一个MTD分区也算一个MTD设备)

 

mtd_notifiers

static struct mtd_notifier *mtd_notifiers = NULL;

标示mtd_notifier队列的指针

 

 

add_mtd_device

格式:

int add_mtd_device(struct mtd_info *mtd)

注释:

/**

 *    add_mtd_device - register an MTD device

 *    @mtd: pointer to new MTD device info structure

 *

 *    Add a device to the list of MTD devices present in the system, and

 *    notify each currently active MTD 'user' of its arrival. Returns

 *    zero on success or 1 on failure, which currently will only happen

 *    if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)

 */

功能:

加入一个MTD原始设备。

参数:

mtd:被加入的原始设备

返回:

       成功:返回0

       mtd_table已满:返回1

说明:

1.  加入此原始设备的mtd_infomtd_table中,

2.  notify所有的MTD设备一个mtd被加入(调用所有的notifier->add(mtd))。

调用:

       notifers->add(mtd)

被调用:

       Flash相关文件(your-flash.c)初始化时(maps子目录下的文件中的init_xxx函数)

源代码:

{

       int i;

 

       down(&mtd_table_mutex);

 

       for (i=0; i< MAX_MTD_DEVICES; i++)

              if (!mtd_table[i])                         //找到mtd_table中的空项

              {

                     struct mtd_notifier *not=mtd_notifiers;

 

                     mtd_table[i] = mtd;               //将其指向新加入的MTD原始设备

                     mtd->index = i;

                     DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);

                     while (not)                                  //如果有notifier,调用所有的notifier对新加

                     {                                               //入的MTD原始设备操作一遍

                            (*(not->add))(mtd);

                            not = not->next;

                     }

                     up(&mtd_table_mutex);

                     MOD_INC_USE_COUNT;

                     return 0;

              }

      

       up(&mtd_table_mutex);

       return 1;

}    

 

del_mtd_device

格式:

       int del_mtd_device (struct mtd_info *mtd)

注释:

/**

 *    del_mtd_device - unregister an MTD device

 *    @mtd: pointer to MTD device info structure

 *

 *    Remove a device from the list of MTD devices present in the system,

 *    and notify each currently active MTD 'user' of its departure.

 *    Returns zero on success or 1 on failure, which currently will happen

 *    if the requested device does not appear to be present in the list.

 */

 

功能:

       删除一个mtd设备。

说明:

       mtd_table中删除此设备的mtd_info,通知所有的MTD设备(调用所有的notifier.removemtd))

参数:

       mtd:描述此设备的mtd_info

返回:

       成功:返回0

       此设备不在mtd_table中:返回1

调用:

       notifiers->remove(mtd)

被调用:

       Flash相关文件(your-flash.c)清除设备时(drivers/mtd/maps子目录下的文件中的cleanupt_xxx函数)

源代码:

{

       struct mtd_notifier *not=mtd_notifiers;

       int i;

      

       down(&mtd_table_mutex);

 

       for (i=0; i < MAX_MTD_DEVICES; i++)

       {

              if (mtd_table[i] == mtd)

              {

                     while (not)                                         //如果有notifier,调用所有的notifier

                     {                                                      //被删除的MTD原始设备操作一遍

                            (*(not->remove))(mtd);

                            not = not->next;

                     }

                     mtd_table[i] = NULL;

                     up (&mtd_table_mutex);

                     MOD_DEC_USE_COUNT;

                     return 0;

              }

       }

 

       up(&mtd_table_mutex);

       return 1;

}

 

 

register_mtd_user

格式:

       void register_mtd_user (struct mtd_notifier *new)

注释:

/**

 *    register_mtd_user - register a 'user' of MTD devices.

 *    @new: pointer to notifier info structure

 *

 *    Registers a pair of callbacks function to be called upon addition

 *    or removal of MTD devices. Causes the 'add' callback to be immediately

 *    invoked for each MTD device currently present in the system.

 */

 

功能:

       加入一个MTD原始设备的使用者(即MTD设备,MTD块设备或字符设备)。

说明:

1.  将新的mtd通知器new加入notifer队列中,

2.  增加模块的use count

3.  对所有已有的MTD原始设备调用new->add()

参数:

       new:新MTD设备的mtd_notifier

返回:

      

调用:

       new->add()

被调用:

       (如果定义了CONFIG_DEVFSMTD设备级的初始化(mtdblock.cmtdchar.cftl.c中的init_xxx

源代码:

{

       int i;

 

       down(&mtd_table_mutex);

 

       new->next = mtd_notifiers;

       mtd_notifiers = new;

 

      MOD_INC_USE_COUNT;

      

       for (i=0; i< MAX_MTD_DEVICES; i++)     //对所有的MTD原始设备操作一遍

              if (mtd_table[i])

                     new->add(mtd_table[i]);

 

       up(&mtd_table_mutex);

}

 

 

unregister_mtd_user

格式:

       int unregister_mtd_user (struct mtd_notifier *old)

注释:

/**

 *    unregister_mtd_user - unregister a 'user' of MTD devices.

 *    @new: pointer to notifier info structure

 *

 *    Removes a callback function pair from the list of 'users' to be

 *    notified upon addition or removal of MTD devices. Causes the

 *    'remove' callback to be immediately invoked for each MTD device

 *    currently present in the system.

 */

功能:

       删除一个MTD设备

说明:

1.  将被删除MTD设备的通知器oldnotifer队列中删除

2.  减模块的use count

3.  对所有已有的MTD原始设备调用old->remove()

参数:

       old:被删除MTD设备的mtd_notifier

返回:

       成功:返回0

       notifiers队列中无此设备的notifier:返回1

调用:

       old->remove()

被调用:

       (如果定义了CONFIG_DEVFSMTD设备级的清除(mtdblock.cmtdchar.cftl.c中的cleanup_xxx

源代码:

{

       struct mtd_notifier **prev = &mtd_notifiers;

       struct mtd_notifier *cur;

       int i;

 

       down(&mtd_table_mutex);

 

       while ((cur = *prev)) {

              if (cur == old) {

                     *prev = cur->next;

 

                     MOD_DEC_USE_COUNT;

 

                     for (i=0; i< MAX_MTD_DEVICES; i++)     //对所有的MTD原始设备调用一遍

                            if (mtd_table[i])                                  //remove函数

                                   old->remove(mtd_table[i]);

                    

                     up(&mtd_table_mutex);

                     return 0;

              }

              prev = &cur->next;

       }

       up(&mtd_table_mutex);

       return 1;

}

 

 

__get_mtd_device

格式:

       struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num)

注释:

/**

 *    __get_mtd_device - obtain a validated handle for an MTD device

 *    @mtd: last known address of the required MTD device

 *    @num: internal device number of the required MTD device

 *

 *    Given a number and NULL address, return the num'th entry in the device

 *    table, if any.    Given an address and num == -1, search the device table

 *    for a device with that address and return if it's still present. Given

 *    both, return the num'th driver only if its address matches. Return NULL

 *    if not. get_mtd_device() increases the use count, but

 *    __get_mtd_device() doesn't - you should generally use get_mtd_device().

 */

功能:

       返回指定的MTD原始设备的handler

说明:

       num==-1&mtd!=NULL:返回与mtd地址相同的设备

       0<num<MAX_MTD_DEVICES&mtd==NULL:返回mtd_table中的第numMTD设备

       0<num<MAX_MTD_DEVICES&mtd!=NULL:如果mtd_table中第num个设备为mtd

返回mtd;否则返回空。

       其余情况:返回NULL

参数:

       mtd:指定MTD原始设备的指针

       num:见说明

返回:

       见说明

调用:

      

被调用:

       get_mtd_device()

源代码:

{

       struct mtd_info *ret = NULL;

       int i;

 

       down(&mtd_table_mutex);

 

       if (num == -1) {

              for (i=0; i< MAX_MTD_DEVICES; i++)

                     if (mtd_table[i] == mtd)

                            ret = mtd_table[i];

       } else if (num < MAX_MTD_DEVICES) {

              ret = mtd_table[num];

              if (mtd && mtd != ret)

                     ret = NULL;

       }

      

       up(&mtd_table_mutex);

       return ret;

}

 

 

mtdpart.c

       MTD原始设备层分区的实现,mtd_part结构是用于描述分区的,由mtd_partitonsmtd_part中的list成员链成一个链表。每个mtd_part结构中的mtd_info结构用于描述本分区,被加入mtd_table中,其中大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。而主分区(其大小涵盖所有分区)则不作为一个MTD原始设备加入mtd_table

       本文件向外提供的add_mtd_partitions()del_mtd_partitions()根据参数map_info结构将所有的分区加入mtd_table

       mtdpart.c中还实现了part_readpart_write等函数,这些函数注册在每个分区中,指向主分区的readwrite函数,之所以这样做而不直接将主分区的readwrite函数连接到每个分区中的原因是因为函数中的参数mtd_info会被调用者置为函数所属的mtd_info,即mtd->read(mtd…),而参数mtd_info其实应该指向主分区。

mtd_partitions

/* Our partition linked list */

static LIST_HEAD(mtd_partitions);                    MTD原始设备分区的链表

 

 

mtd_part

描述分区的结构

/* Our partition node structure */

struct mtd_part {         

       struct mtd_info mtd;             分区的信息(大部分由其master决定)

       struct mtd_info *master;              该分区的主分区

       u_int32_t offset;                   该分区的偏移地址

       int index;                             分区号

       struct list_head list;

};

 

 

PART(x)

/*

 * Given a pointer to the MTD object in the mtd_part structure, we can retrieve

 * the pointer to that structure with this macro.

 */

#define PART(x)  ((struct mtd_part *)(x))         mtd_info结构转换为mtd_part结构

 

 

add_mtd_partitions

格式:

       int add_mtd_partitions(struct mtd_info *master, struct mtd_partition *parts, int nbparts)

注释:

/*

 * This function, given a master MTD object and a partition table, creates

 * and registers slave MTD objects which are bound to the master according to

 * the partition definitions.

 * (Q: should we register the master MTD object as well?)

 */

功能:

       MTD原始设备分区

说明:

       打印信息(型如“Creating 3 MTD partitions on "Your Flash map”),

       对每一个新建分区{

建立一个新的mtd_part结构,并将其加入mtd_partitions中,

根据master设置mtd_part.mtd_info的成员,

              打印信息(型如“0x00020000-0x00800000 : "Your Kernel"”),

              进行安全性检查,

              根据master->numeraseregions处理多erasesize区域的事务,

              调用add_mtd_device将此分区作为MTD设备加入mtd_table

       }

参数:

       master:指定的MTD设备

       parts:关于分区信息的指针

       nbparts:分区的数目

返回:

       成功:返回0

       分配mtd_part时内存不足:返回-ENOMEM

调用:

       add_mtd_device()

       del_mtd_partitions()

被调用:

       flash相关的初始化函数中(maps子目录下的文件的函数init_xxx中)

源代码:

{

       struct mtd_part *slave;

       u_int32_t cur_offset = 0;

       int i;

 

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

 

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

 

              /* allocate the partition structure */

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

              if (!slave) {

                     printk ("memory allocation error while creating partitions for \"%s\"\n",

                            master->name);

                     del_mtd_partitions(master);

                     return -ENOMEM;

              }

              memset(slave, 0, sizeof(*slave));

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

 

              /* set up the MTD object for this partition */

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

              slave->mtd.flags = master->flags & ~parts[i].mask_flags;

              slave->mtd.size = parts[i].size;

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

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

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

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

 

              slave->mtd.name = parts[i].name;

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

 

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

 

              slave->mtd.read = part_read;

              slave->mtd.write = part_write;

              if (master->sync)

                     slave->mtd.sync = part_sync;

              if (!i && master->suspend && master->resume) {

                            slave->mtd.suspend = part_suspend;

                            slave->mtd.resume = part_resume;

              }

 

              if (master->writev)

                     slave->mtd.writev = part_writev;

              if (master->readv)

                     slave->mtd.readv = part_readv;

              if (master->lock)

                     slave->mtd.lock = part_lock;

              if (master->unlock)

                     slave->mtd.unlock = part_unlock;

              slave->mtd.erase = part_erase;

              slave->master = master;

              slave->offset = parts[i].offset;

              slave->index = i;

 

              if (slave->offset == MTDPART_OFS_APPEND)

                     slave->offset = cur_offset;

              if (slave->mtd.size == MTDPART_SIZ_FULL)

                     slave->mtd.size = master->size - slave->offset;

              cur_offset = slave->offset + slave->mtd.size;

      

              printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,

                     slave->offset + slave->mtd.size, slave->mtd.name);

 

              /* let's do some sanity checks */          //安全检查

              if (slave->offset >= master->size) {

                            /* let's register it anyway to preserve ordering */

                     slave->offset = 0;

                     slave->mtd.size = 0;

                     printk ("mtd: partition \"%s\" is out of reach -- disabled\n",

                            parts[i].name);

              }

              if (slave->offset + slave->mtd.size > master->size) {

                     slave->mtd.size = master->size - slave->offset;

                     printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",

                            parts[i].name, master->name, slave->mtd.size);

              }

              if (master->numeraseregions>1) {

                     /* Deal with variable erase size stuff */

                     int i;

                     struct mtd_erase_region_info *regions = master->eraseregions;

                    

                     /* Find the first erase regions which is part of this partition. */

                     for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)

                            ;

 

                     for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {

                            if (slave->mtd.erasesize < regions[i].erasesize) {

                                   slave->mtd.erasesize = regions[i].erasesize;

                            }

                     }

              } else {

                     /* Single erase size */

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

              }

 

              if ((slave->mtd.flags & MTD_WRITEABLE) &&

                  (slave->offset % slave->mtd.erasesize)) {

                     /* Doesn't start on a boundary of major erase size */

                     /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */

                     slave->mtd.flags &= ~MTD_WRITEABLE;

                     printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",parts[i].name);

              }

              if ((slave->mtd.flags & MTD_WRITEABLE) &&

                  (slave->mtd.size % slave->mtd.erasesize)) {

                     slave->mtd.flags &= ~MTD_WRITEABLE;

                     printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", parts[i].name);

              }

 

              /* register our partition */

              add_mtd_device(&slave->mtd);

       }

 

       return 0;

}

 

 

del_mtd_partitions

格式:

       int del_mtd_partitions(struct mtd_info *master)

注释:

/*

 * This function unregisters and destroy all slave MTD objects which are

 * attached to the given master MTD object.

 */  

功能:

       删除master上的所有分区。

说明:

       mtd_partitions上的每一个分区{

       如果它的主分区是master,将它从mtd_partitionsmtd_table中删除并free

}

参数:

       master:被删除分区的主分区

返回:

       返回0

调用:

       del_mtd_device()

       kfree()

被调用:

       flash相关的清除函数(maps子目录下的文件中的cleanup_xxx函数)

源代码:

{

       struct list_head *node;

       struct mtd_part *slave;

 

       for (node = mtd_partitions.next;

            node != &mtd_partitions;

            node = node->next) {

              slave = list_entry(node, struct mtd_part, list);

              if (slave->master == master) {

                     struct list_head *prev = node->prev;

                     __list_del(prev, node->next);        //mtd_partitions中删除

                     del_mtd_device(&slave->mtd);     //mtd_table中删除

                     kfree(slave);

                     node = prev;

              }

       }

 

       return 0;

}

 

 

part_read

part_write

part_readv

part_writev

part_erase

part_lock

part_unlock

part_sync

part_suspend

part_resume

格式:

      

注释:

/*

 * MTD methods which simply translate the effective address and pass through

 * to the _real_ device.

 */

功能:

       非主分区的xxxx函数。

说明:

       进行安全性检查,

       调用主分区的xxxx函数

参数:

      

返回:

      

调用:

      

被调用:

       add_mtd_partitions中被注册进非主分区的md_info

源代码:

      

 

 

mtdblock.c

       MTD设备层的块设备的相关数据结构和程序,其中定义了MTD块设备的notifierMTD块设备的结构mtdblk_dev类型的mtdblks数组,该数组中的每个成员与MTD原始设备层的mtd_table数组中的每个成员一一对应。

notifier

       设备层的mtdblcok设备的notifier

static struct mtd_notifier notifier = {   

        mtd_notify_add,

        mtd_notify_remove,

        NULL

};

 

 

mtdblk_dev

设备层的块设备的结构(and its private , I think),MTD字符设备没有相对应的结构。

struct mtdblk_dev {                    

       struct mtd_info *mtd; /* Locked */      下层原始设备层的MTD设备结构

       int count;

       struct semaphore cache_sem;

       unsigned char *cache_data;                 缓冲区(与MTD设备块对齐)

       unsigned long cache_offset;                缓冲区内数据在原始设备层MTD设备内的偏移

       unsigned int cache_size;                      缓冲区大小(通常被设置为MTD设备的erasesize

       enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;        缓冲区状态

}           

 

mtdblks

设备层(块)设备数组(与原始设备层的MAX_MTD_DEVICESMTD设备一一对应)

static struct mtdblk_dev *mtdblks[MAX_MTD_DEVICES];     

 

 

erase_callback

格式:

       static void erase_callback(struct erase_info *done)

注释:

/*

 * Cache stuff...

 *

 * Since typical flash erasable sectors are much larger than what Linux's

 * buffer cache can handle, we must implement read-modify-write on flash

 * sectors for each block write requests.  To avoid over-erasing flash sectors

 * and to speed things up, we locally cache a whole flash sector while it is

 * being written to until a different sector is required.

 */

功能:

       回调函数,当写完时被调用。

说明:

       erase_write中被赋给erase_info结构

参数:

       done

返回:

      

调用:

       wake_up

被调用:

       erase_write中被注册

源代码:

{

       wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;

       wake_up(wait_q);

}

 

 

erase_write

格式:

       static int erase_write (struct mtd_info *mtd, unsigned long pos,       int len, const char *buf)

注释:

      

功能:

       擦除并写flash

说明:

1.  声明一个erase_info变量erase并给其赋值,

2.  调用原始设备层函数mtd_info->erase(mtd,erase)

3.  调用原始设备层函数mtd_info->write()

参数:

       mtd:被操作的MTD原始设备

       posMTD原始设备的起始位置

       len:长度

       buf:被写入的内容

返回:

       FIXME

调用:

       mtd_info->erase

       mtd_info->write

被调用:

       write_cached_data

       do_cached_write

源代码:

{

       struct erase_info erase;

       DECLARE_WAITQUEUE(wait, current);

       wait_queue_head_t wait_q;

       size_t retlen;

       int ret;

 

       /*

        * First, let's erase the flash block.

        */

 

       init_waitqueue_head(&wait_q);

       erase.mtd = mtd;

       erase.callback = erase_callback;

       erase.addr = pos;

       erase.len = len;

       erase.priv = (u_long)&wait_q;

 

       set_current_state(TASK_INTERRUPTIBLE);

       add_wait_queue(&wait_q, &wait);

 

       ret = MTD_ERASE(mtd, &erase);              //调用mtd->erase()

       if (ret) {

              set_current_state(TASK_RUNNING);

              remove_wait_queue(&wait_q, &wait);

              printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "

                                 "on \"%s\" failed\n",

                     pos, len, mtd->name);

              return ret;

       }

 

       schedule();  /* Wait for erase to finish. */

       remove_wait_queue(&wait_q, &wait);

 

       /*

        * Next, writhe data to flash.

        */

 

       ret = MTD_WRITE (mtd, pos, len, &retlen, buf);              //调用mtd->write()

       if (ret)

              return ret;

       if (retlen != len)

              return -EIO;

       return 0;

}

 

 

write_cached_data

格式:

       static int write_cached_data (struct mtdblk_dev *mtdblk)

注释:

      

功能:

       将指定设备层MTD块设备缓冲区内的数据写入MTD设备

说明:

      

参数:

       mtdblk:指定的设备层MTD块设备结构

返回:

       成功:返回0

       失败:FIXME

调用:

       erase_write()写入MTD设备

被调用:

       do_cached_write()

       mtdblock_release()

       mtdblock_ioctl()

源代码:

{

       struct mtd_info *mtd = mtdblk->mtd;

       int ret;

 

       if (mtdblk->cache_state != STATE_DIRTY)

              return 0;

 

       DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" "

                     "at 0x%lx, size 0x%x\n", mtd->name,

                     mtdblk->cache_offset, mtdblk->cache_size);

      

       ret = erase_write (mtd, mtdblk->cache_offset,

                        mtdblk->cache_size, mtdblk->cache_data);

       if (ret)

              return ret;

 

       /*

        * Here we could argably set the cache state to STATE_CLEAN.

        * However this could lead to inconsistency since we will not

        * be notified if this content is altered on the flash by other

        * means.  Let's declare it empty and leave buffering tasks to

        * the buffer cache instead.

        */

       mtdblk->cache_state = STATE_EMPTY;

       return 0;

}

 

do_cached_write

格式:

       static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,

                         int len, const char *buf)

注释:

      

功能:

       buf向指定的设备层MTD块设备中缓冲写

说明:

       如果缓冲区大小为0,则调用mtd_info->write()直接向MTD设备中写,

       当(len>0{

              如果是整块block,则直接向MTD设备中写

              否则{

先调用write_cached_data()将缓冲内的数据写入MTD设备

                     在调用mtd_info->readMTD设备中将对应块的数据读入缓冲

                     buf的数据(非整块)写入缓冲

              }

              len自减

       }

参数:

       mtdblk:指定的MTD块设备

       pos:写入原始设备层MTD设备的位置

       len:数据长度

       buf:被写入的数据

返回:

       成功:返回0

       失败:返回错误码

调用:

       erase_write()用于直接写MTD设备

       write_cached_data()用于清空MTD块设备的缓冲区

       mtd_info->read()用于从MTD设备中读数据

       mtd_info->write()用于向MTD设备中写数据

被调用:

       handle_mtdblock_request()

源代码:

{

       struct mtd_info *mtd = mtdblk->mtd;

       unsigned int sect_size = mtdblk->cache_size;

       size_t retlen;

       int ret;

 

       DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",

              mtd->name, pos, len);

      

       if (!sect_size)

              return MTD_WRITE (mtd, pos, len, &retlen, buf);

 

       while (len > 0) {

              unsigned long sect_start = (pos/sect_size)*sect_size;//we should write by erase block

              unsigned int offset = pos - sect_start;//real start address

              unsigned int size = sect_size - offset;

              if( size > len )

                     size = len;

 

              if (size == sect_size) {

                     /*

                      * We are covering a whole sector.  Thus there is no

                      * need to bother with the cache while it may still be

                      * useful for other partial writes.

                      */

                     ret = erase_write (mtd, pos, size, buf);

                     if (ret)

                            return ret;

              } else {

                     /* Partial sector: need to use the cache */

 

                     if (mtdblk->cache_state == STATE_DIRTY &&

                         mtdblk->cache_offset != sect_start) {

                            ret = write_cached_data(mtdblk);

                            if (ret)

                                   return ret;

                     }

 

                     if (mtdblk->cache_state == STATE_EMPTY ||

                         mtdblk->cache_offset != sect_start) {

                            /* fill the cache with the current sector */

                            mtdblk->cache_state = STATE_EMPTY;

                            ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);

                            if (ret)

                                   return ret;

                            if (retlen != sect_size)

                                   return -EIO;

 

                            mtdblk->cache_offset = sect_start;

                            mtdblk->cache_size = sect_size;

                            mtdblk->cache_state = STATE_CLEAN;

                     }

 

                     /* write data to our local cache */

                     memcpy (mtdblk->cache_data + offset, buf, size);

                     mtdblk->cache_state = STATE_DIRTY;

              }

 

              buf += size;

              pos += size;

              len -= size;

       }

 

       return 0;

}

 

 

do_cached_read

格式:

       static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,

                        int len, char *buf)

注释:

      

功能:

       从指定的MTD块设备中缓冲读到指定位置buf

说明:

       如果缓冲区大小为0,调用mtd_info->read()直接从原始设备层MTD设备中读取

       否则当(len>0{

              如果缓冲区不为空且其中数据正好是所需数据,则从缓冲区拷贝到buf

              否则调用mtd_info->read()MTD中读

       }

参数:

       mtdblk:指定的MTD块设备

       posMTD设备中指定的位置

       len:长度

       buf:被写入的地址

返回:

       成功:返回0

       失败:返回错误码

调用:

       mtd_info->read()从指定的MTD设备中读

被调用:

       handle_mtdblock_request()

源代码:

{

       struct mtd_info *mtd = mtdblk->mtd;

       unsigned int sect_size = mtdblk->cache_size;

       size_t retlen;

       int ret;

 

       DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",

                     mtd->name, pos, len);

      

       if (!sect_size)

              return MTD_READ (mtd, pos, len, &retlen, buf);

 

       while (len > 0) {

              unsigned long sect_start = (pos/sect_size)*sect_size;

              unsigned int offset = pos - sect_start;

              unsigned int size = sect_size - offset;

              if (size > len)

                     size = len;

 

              /*

               * Check if the requested data is already cached

               * Read the requested amount of data from our internal cache if it

               * contains what we want, otherwise we read the data directly

               * from flash.

               */

              if (mtdblk->cache_state != STATE_EMPTY &&

                  mtdblk->cache_offset == sect_start) {

                     memcpy (buf, mtdblk->cache_data + offset, size);

              } else {

                     ret = MTD_READ (mtd, pos, size, &retlen, buf);

                     if (ret)

                            return ret;

                     if (retlen != size)

                            return -EIO;

              }

 

              buf += size;

              pos += size;

              len -= size;

       }

 

       return 0;

}

 

 

mtdblock_open

格式:

       static int mtdblock_open(struct inode *inode, struct file *file)

注释:

      

功能:

       打开一个MTD块设备(设备层)

说明:

       获得指定节点在原始设备层的MTD设备(mtd_table),

如果 其在设备层的对应块设备(mtdblks)被打开,返回,

否则 将其打开并初始化,

MTD设备的大小/1024赋给对应的mtd_sizes

MTD设备的erasesizePAGE_SIZE中较小的一个赋给对应的mtd_blksizes

参数:

       inode:指定的索引节点

       file:没用

返回:

       成功:返回0

       失败:返回错误码

调用:

       get_mtd_device()put_mtd_device()获取原始设备层的MTD设备

被调用:

       注册进mtd_fops结构

源代码:

{

       struct mtdblk_dev *mtdblk;

       struct mtd_info *mtd;

       int dev;

 

       DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");

      

       if (!inode)

              return -EINVAL;

      

       dev = MINOR(inode->i_rdev);

       if (dev >= MAX_MTD_DEVICES)

              return -EINVAL;

 

       MOD_INC_USE_COUNT;

 

       mtd = get_mtd_device(NULL, dev);

       if (!mtd)

              return -ENODEV;

       if (MTD_ABSENT == mtd->type) {

              put_mtd_device(mtd);

              MOD_DEC_USE_COUNT;

              return -ENODEV;

       }

      

       spin_lock(&mtdblks_lock);

 

       /* If it's already open, no need to piss about. */

       if (mtdblks[dev]) {

              mtdblks[dev]->count++;

              spin_unlock(&mtdblks_lock);

              return 0;

       }

      

       /* OK, it's not open. Try to find it */

 

       /* First we have to drop the lock, because we have to

          to things which might sleep.

       */

       spin_unlock(&mtdblks_lock);

 

       mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);

       if (!mtdblk) {

              put_mtd_device(mtd);

              MOD_DEC_USE_COUNT;

              return -ENOMEM;

       }

       memset(mtdblk, 0, sizeof(*mtdblk));

       mtdblk->count = 1;

       mtdblk->mtd = mtd;

 

       init_MUTEX (&mtdblk->cache_sem);

       mtdblk->cache_state = STATE_EMPTY;

       if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&

           mtdblk->mtd->erasesize) {

              mtdblk->cache_size = mtdblk->mtd->erasesize;

              mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);

              if (!mtdblk->cache_data) {

                     put_mtd_device(mtdblk->mtd);

                     kfree(mtdblk);

                     MOD_DEC_USE_COUNT;

                     return -ENOMEM;

              }

       }

 

       /* OK, we've created a new one. Add it to the list. */

 

       spin_lock(&mtdblks_lock);

 

       if (mtdblks[dev]) {

              /* Another CPU made one at the same time as us. */

              mtdblks[dev]->count++;

              spin_unlock(&mtdblks_lock);

              put_mtd_device(mtdblk->mtd);

              vfree(mtdblk->cache_data);

              kfree(mtdblk);

              return 0;

       }

 

       mtdblks[dev] = mtdblk;

       mtd_sizes[dev] = mtdblk->mtd->size/1024;

       if (mtdblk->mtd->erasesize)

              mtd_blksizes[dev] = mtdblk->mtd->erasesize;

       if (mtd_blksizes[dev] > PAGE_SIZE)

              mtd_blksizes[dev] = PAGE_SIZE;

       set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE));

      

       spin_unlock(&mtdblks_lock);

      

       DEBUG(MTD_DEBUG_LEVEL1, "ok\n");

 

       return 0;

}

 

      

 

mtdblock_release

格式:

       static release_t mtdblock_release(struct inode *inode, struct file *file)

注释:

      

功能:

       释放一个设备层的MTD块设备

说明:

       调用write_cached_data刷新缓冲区,

       MTD块设备使用数减1,如果变为0,释放原始设备层的MTD设备

参数:

       inode:指定的索引节点

       file:无用

返回:

       成功:返回0

       inodeNULL:返回-ENODEV

调用:

       write_cached_data()刷新缓冲

       put_mtd_device()释放MTD设备

被调用:

       注册进mtd_fops结构

源代码:

{

       int dev;

       struct mtdblk_dev *mtdblk;

     DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");

 

       if (inode == NULL)

              release_return(-ENODEV);

 

       invalidate_device(inode->i_rdev, 1);

 

       dev = MINOR(inode->i_rdev);

       mtdblk = mtdblks[dev];

 

       down(&mtdblk->cache_sem);

       write_cached_data(mtdblk);

       up(&mtdblk->cache_sem);

 

       spin_lock(&mtdblks_lock);

       if (!--mtdblk->count) {

              /* It was the last usage. Free the device */

              mtdblks[dev] = NULL;

              spin_unlock(&mtdblks_lock);

              if (mtdblk->mtd->sync)

                     mtdblk->mtd->sync(mtdblk->mtd);

              put_mtd_device(mtdblk->mtd);

              vfree(mtdblk->cache_data);

              kfree(mtdblk);

       } else {

              spin_unlock(&mtdblks_lock);

       }

 

       DEBUG(MTD_DEBUG_LEVEL1, "ok\n");

 

       MOD_DEC_USE_COUNT;

       release_return(0);

} 

 

 

handle_mtdblock_request

格式:

       static void handle_mtdblock_request(void)

注释:

/*

 * This is a special request_fn because it is executed in a process context

 * to be able to sleep independently of the caller.  The io_request_lock

 * is held upon entry and exit.

 * The head of our request queue is considered active so there is no need

 * to dequeue requests before we are done.

 */

功能:

       处理对MTD块设备的请求

说明:

       获取当前请求,分情况处理

参数:

      

返回:

      

调用:

       do_cached_read()处理读请求

       do_cached_write()处理写请求

被调用:

       mtdblock_thread()

源代码:

{

       struct request *req;

       struct mtdblk_dev *mtdblk;

       unsigned int res;

 

       for (;;) {

              INIT_REQUEST;

              req = CURRENT;

              spin_unlock_irq(&io_request_lock);

              mtdblk = mtdblks[MINOR(req->rq_dev)];

              res = 0;

 

              if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES)

                     panic(__FUNCTION__": minor out of bound");

 

              if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))

                     goto end_req;

 

              // Handle the request

              switch (req->cmd)

              {

                     int err;

 

                     case READ:

                     down(&mtdblk->cache_sem);

                     err = do_cached_read (mtdblk, req->sector << 9,

                                   req->current_nr_sectors << 9,

                                   req->buffer);

                     up(&mtdblk->cache_sem);

                     if (!err)

                            res = 1;

                     break;

 

                     case WRITE:

                     // Read only device

                     if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) )

                            break;

 

                     // Do the write

                     down(&mtdblk->cache_sem);

                     err = do_cached_write (mtdblk, req->sector << 9,

                                   req->current_nr_sectors << 9,

                                   req->buffer);

                     up(&mtdblk->cache_sem);

                     if (!err)

                            res = 1;

                     break;

              }

 

end_req:

              spin_lock_irq(&io_request_lock);

              end_request(res);

       }

}

 

leaving

static volatile int leaving = 0;         控制线程的变量,在mtdblock_thread作为测试条件,在cleanup_mtdblock中被赋1

mtdblock_thread

格式:

       int mtdblock_thread(void *dummy)

注释:

      

功能:

       启动一个MTD块设备线程

说明:

       调用handle_mtdblock_request()处理对MTD块设备的读写

参数:

       dummyFIXME

返回:

       线程结束:返回0

调用:

       handle_mtdblock_request()

被调用:

       init_mtdblock()

源代码:

{

       struct task_struct *tsk = current;

       DECLARE_WAITQUEUE(wait, tsk);

 

       tsk->session = 1;

       tsk->pgrp = 1;

       /* we might get involved when memory gets low, so use PF_MEMALLOC */

       tsk->flags |= PF_MEMALLOC;

       strcpy(tsk->comm, "mtdblockd");

       tsk->tty = NULL;

       spin_lock_irq(&tsk->sigmask_lock);

       sigfillset(&tsk->blocked);

       recalc_sigpending(tsk);

       spin_unlock_irq(&tsk->sigmask_lock);

       exit_mm(tsk);

       exit_files(tsk);

       exit_sighand(tsk);

       exit_fs(tsk);

 

       while (!leaving) {

              add_wait_queue(&thr_wq, &wait);

              set_current_state(TASK_INTERRUPTIBLE);

              spin_lock_irq(&io_request_lock);

              if (QUEUE_EMPTY || QUEUE_PLUGGED) {

                     spin_unlock_irq(&io_request_lock);

                     schedule();

                     remove_wait_queue(&thr_wq, &wait);

              } else {

                     remove_wait_queue(&thr_wq, &wait);

                     set_current_state(TASK_RUNNING);

                     handle_mtdblock_request();

                     spin_unlock_irq(&io_request_lock);

              }

       }

 

       up(&thread_sem);

       return 0;

}

 

 

mtdblock_ioctl

格式:

static int mtdblock_ioctl(struct inode * inode, struct file * file,

                    unsigned int cmd, unsigned long arg)

注释:

      

功能:

       MTD块设备的IO控制

说明:

      

参数:

       FIXME

返回:

       成功:返回0

       失败:返回错误码

调用:

       write_cached_data()

被调用:

       被注册进mtd_fops结构

源代码:

{

       struct mtdblk_dev *mtdblk;

 

       mtdblk = mtdblks[MINOR(inode->i_rdev)];

 

#ifdef PARANOIA

       if (!mtdblk)

              BUG();

#endif

 

       switch (cmd) {

       case BLKGETSIZE:   /* Return device size */

              return put_user((mtdblk->mtd->size >> 9), (long *) arg);

 

#ifdef BLKGETSIZE64

       case BLKGETSIZE64:

              return put_user((u64)mtdblk->mtd->size, (u64 *)arg);

#endif

             

       case BLKFLSBUF:

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)

              if(!capable(CAP_SYS_ADMIN))

                     return -EACCES;

#endif

              fsync_dev(inode->i_rdev);

              invalidate_buffers(inode->i_rdev);

              down(&mtdblk->cache_sem);

              write_cached_data(mtdblk);

              up(&mtdblk->cache_sem);

              if (mtdblk->mtd->sync)

                     mtdblk->mtd->sync(mtdblk->mtd);

              return 0;

 

       default:

              return -EINVAL;

       }

}

 

mtd_fops

MTD块设备操作的函数结构

static struct block_device_operations mtd_fops =     

{

       open: mtdblock_open,

       release: mtdblock_release,

       ioctl: mtdblock_ioctl

};

 

 

init_mtdblock

格式:

       int __init init_mtdblock(void)

注释:

      

功能:

       初始化MTD块设备

说明:

       如果定义了CONFIG_DEVFS_FS{

              调用devfs_register_blkdev()注册块设备,

              调用register_mtd_user()将设备层MTD块设备的notifier注册进原始设备层

}

否则调用register_blkdev()注册块设备,

       初始化mtd_sizesmtd_blksizes数组,

       启动mtdblock_thread线程

参数:

      

返回:

       成功:返回0

       注册块设备失败:返回-EAGAIN

调用:

       kernel_thread()启动mtdblock_thread线程

       register_blkdev()注册块设备

被调用:

       module_init

       __init

源代码:

{

       int i;

 

       spin_lock_init(&mtdblks_lock);

#ifdef CONFIG_DEVFS_FS

       if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))

       {

              printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",

                     MTD_BLOCK_MAJOR);

              return -EAGAIN;

       }

 

       devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);

       register_mtd_user(&notifier);

#else

       if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {

              printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",

                     MTD_BLOCK_MAJOR);

              return -EAGAIN;

       }

#endif

      

       /* We fill it in at open() time. */

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

              mtd_sizes[i] = 0;

              mtd_blksizes[i] = BLOCK_SIZE;

       }

       init_waitqueue_head(&thr_wq);

       /* Allow the block size to default to BLOCK_SIZE. */

       blksize_size[MAJOR_NR] = mtd_blksizes;

       blk_size[MAJOR_NR] = mtd_sizes;

      

       blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);

       kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);

       return 0;

}

 

cleanup_mtdblock

格式:

       static void __exit cleanup_mtdblock(void)

注释:

      

功能:

       清除MTD块设备

说明:

       如果定义了CONFIG_DEVFS_FS{

              调用unregister_mtd_user()注销MTD块设备的notifier

              调用devfs_unregister_blkdev()注销MTD块设备

       }

否则调用unregister_blkdev()注销MTD块设备

参数:

      

返回:

      

调用:

       unregister_blkdev()注销MTD块设备

被调用:

       __exit

       module_exit

源代码:

{

       leaving = 1;

       wake_up(&thr_wq);

       down(&thread_sem);

#ifdef CONFIG_DEVFS_FS

       unregister_mtd_user(&notifier);

       devfs_unregister(devfs_dir_handle);

       devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);

#else

       unregister_blkdev(MAJOR_NR,DEVICE_NAME);

#endif

       blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));

       blksize_size[MAJOR_NR] = NULL;

       blk_size[MAJOR_NR] = NULL;

}

 

 

 

mtdchar.c

MTD字符设备的文件

notifier

MTD字符设备的notifier

static struct mtd_notifier notifier = {          

       add: mtd_notify_add,

       remove:   mtd_notify_remove,

};

 

mtd_lseek

格式:

       static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)

注释:

      

功能:

       MTD字符设备的指针

说明:

      

参数:

       fileMTD字符设备文件

       offset:偏移

       orig:设置方式

返回:

       成功:返回当前指针位置

       设置方式无效:返回-EINVAL

调用:

      

被调用:

       注册进mtd_fops结构

源代码:

{

       struct mtd_info *mtd=(struct mtd_info *)file->private_data;

 

       switch (orig) {

       case 0:

              /* SEEK_SET */

              file->f_pos = offset;

              break;

       case 1:

              /* SEEK_CUR */

              file->f_pos += offset;

              break;

       case 2:

              /* SEEK_END */

              file->f_pos =mtd->size + offset;

              break;

       default:

              return -EINVAL;

       }

 

       if (file->f_pos < 0)

              file->f_pos = 0;

       else if (file->f_pos >= mtd->size)

              file->f_pos = mtd->size - 1;

 

       return file->f_pos;

}

 

 

mtd_open

格式:

       static int mtd_open(struct inode *inode, struct file *file)

注释:

      

功能:

       打开一个MTD字符设备

说明:

       devnum=minor>>2(参看Documentations/devices.txt),

       进行安全性检查,

       调用get_mtd_device获取MTD设备,并将file->private_data指向它

参数:

       inodeFIXME

       file:是系统提供给MTD字符设备用于传递参数的file结构,此函数中它的private_data

成员被指向原始设备层的MTD设备

返回:

       成功:返回0

       失败:返回错误码

调用:

       get_mtd_device()获得原始设备层的MTD设备

被调用:

       注册进mtd_fops结构

源代码:

{

       int minor = MINOR(inode->i_rdev);

       int devnum = minor >> 1;

       struct mtd_info *mtd;

 

       DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");

 

       if (devnum >= MAX_MTD_DEVICES)

              return -ENODEV;

 

       /* You can't open the RO devices RW */

       if ((file->f_mode & 2) && (minor & 1))

              return -EACCES;

 

       mtd = get_mtd_device(NULL, devnum);

      

       if (!mtd)

              return -ENODEV;

      

       if (MTD_ABSENT == mtd->type) {

              put_mtd_device(mtd);

              return -ENODEV;

       }

 

       file->private_data = mtd;

             

       /* You can't open it RW if it's not a writeable device */

       if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {

              put_mtd_device(mtd);

              return -EACCES;

       }

             

       return 0;

} /* mtd_open */

 

 

 

 

mtd_close

格式:

       static int mtd_close(struct inode *inode, struct file *file)

注释:

      

功能:

       关闭一个MTD字符设备

说明:

       调用mtd_info->sync()同步MTD设备,

       调用put_mtd_device()返还MTD设备

参数:

       inodeFIXME

       file:无用

返回:

       返回0

调用:

       mtd_info->sync()同步MTD设备

put_mtd_device()返还MTD设备

被调用:

       被注册进mtd_fops结构

源代码:

{

       struct mtd_info *mtd;

 

       DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");

 

       mtd = (struct mtd_info *)file->private_data;

      

       if (mtd->sync)

              mtd->sync(mtd);

      

       put_mtd_device(mtd);

 

       return 0;

} /* mtd_close */

 

MAX_KMALLOC_SIZE

/* FIXME: This _really_ needs to die. In 2.5, we should lock the

   userspace buffer down and use it directly with readv/writev.

*/

#define MAX_KMALLOC_SIZE 0x20000

 

 

mtd_read

格式:

       static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)

注释:

      

功能:

       MTD字符设备的写操作

说明:

       count>0{

              裁减本次操作大小lenmin(MAX_KMALLOC_SIZE,count)

              申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf

              调用mtd_info->readMTD设备中的数据读入kbuf

              kbuf中的数据拷贝到用户空间buf

              count自减

              释放kbuf

       }

参数:

file:系统给MTD字符设备驱动程序用于传递参数的file结构,此函数通过file得到下

层的MTD设备

       buf:用户空间的指针,用于存放读取的数据

       count:被读数据的长度

       ppos:被读数据在MTD设备中的位置

返回:

       成功:返回实际读取数据的长度

       失败:返回错误码

调用:

       mtd_info->read()用于从MTD设备中读取数据

被调用:

       被注册进mtd_fops结构

源代码:

{

       struct mtd_info *mtd = (struct mtd_info *)file->private_data;

       size_t retlen=0;

       size_t total_retlen=0;

       int ret=0;

       int len;

       char *kbuf;

      

       DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");

 

       if (*ppos + count > mtd->size)

              count = mtd->size - *ppos;

 

       if (!count)

              return 0;

      

       /* FIXME: Use kiovec in 2.5 to lock down the user's buffers

          and pass them directly to the MTD functions */

       while (count) {

              if (count > MAX_KMALLOC_SIZE)

                     len = MAX_KMALLOC_SIZE;

              else

                     len = count;

 

              kbuf=kmalloc(len,GFP_KERNEL);

              if (!kbuf)

                     return -ENOMEM;

             

              ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);

              if (!ret) {

                     *ppos += retlen;

                     if (copy_to_user(buf, kbuf, retlen)) {

                             kfree(kbuf);

                            return -EFAULT;

                     }

                     else

                            total_retlen += retlen;

 

                     count -= retlen;

                     buf += retlen;

              }

              else {

                     kfree(kbuf);

                     return ret;

              }

             

              kfree(kbuf);

       }

      

       return total_retlen;

} /* mtd_read */

 

 

 

mtd_write

格式:

       static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)

注释:

      

功能:

       MTD字符设备的写操作

说明:

       count>0{

              裁减本次操作大小lenmin(MAX_KMALLOC_SIZE,count)

              申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf

              将用户空间buf中的数据拷贝到kbuf

              调用mtd_info->writekbuf中的数据读入MTD设备,

              count自减

              释放kbuf

       }

参数:

file:系统给MTD字符设备驱动程序用于传递参数的file结构,此函数通过file得到下

层的MTD设备

       buf:用户空间的指针,用于存放将要写入的数据

       count:被写数据的长度

       ppos:数据被写入MTD设备中的位置

返回:

       成功:返回实际读取数据的长度

       失败:返回错误码

调用:

       mtd_info->write用于写入MTD设备

被调用:

       被注册进mtd_fops结构

源代码:

{

       struct mtd_info *mtd = (struct mtd_info *)file->private_data;

       char *kbuf;

       size_t retlen;

       size_t total_retlen=0;

       int ret=0;

       int len;

 

       DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");

      

       if (*ppos == mtd->size)

              return -ENOSPC;

      

       if (*ppos + count > mtd->size)

              count = mtd->size - *ppos;

 

       if (!count)

              return 0;

 

       while (count) {

              if (count > MAX_KMALLOC_SIZE)

                     len = MAX_KMALLOC_SIZE;

              else

                     len = count;

 

              kbuf=kmalloc(len,GFP_KERNEL);

              if (!kbuf) {

                     printk("kmalloc is null\n");

                     return -ENOMEM;

              }

 

              if (copy_from_user(kbuf, buf, len)) {

                     kfree(kbuf);

                     return -EFAULT;

              }

             

               ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);

              if (!ret) {

                     *ppos += retlen;

                     total_retlen += retlen;

                     count -= retlen;

                     buf += retlen;

              }

              else {

                     kfree(kbuf);

                     return ret;

              }

             

              kfree(kbuf);

       }

 

       return total_retlen;

} /* mtd_write */

 

 

mtd_erase_callback

格式:

       static void mtd_erase_callback (struct erase_info *instr)

注释:

      

功能:

       唤醒进程

说明:

       在擦除进行完后被调用

参数:

       instr:进行的擦除的erase_info结构

返回:

      

调用:

       wake_up()

被调用:

       mtd_ioctl中被赋给erase_info->callback

源代码:

{

       wake_up((wait_queue_head_t *)instr->priv);

}

 

 

mtd_ioctl

格式:

       static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)

注释:

      

功能:

       MTD字符设备的IO控制

说明:

       根据cmd分别处理

参数:

       inodeFIXME

       file:传递参数的结构

       cmdIO控制的命令

       argIO控制的参数

返回:

       成功:返回0

       失败:返回错误码

调用:

      

被调用:

       注册进mtd_fops结构

源代码:

{

       struct mtd_info *mtd = (struct mtd_info *)file->private_data;

       int ret = 0;

       u_long size;

      

       DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");

 

       size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;

       if (cmd & IOC_IN) {

              ret = verify_area(VERIFY_READ, (char *)arg, size);

              if (ret) return ret;

       }

       if (cmd & IOC_OUT) {

              ret = verify_area(VERIFY_WRITE, (char *)arg, size);

              if (ret) return ret;

       }

      

       switch (cmd) {

       case MEMGETREGIONCOUNT:

              if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))

                     return -EFAULT;

              break;

 

       case MEMGETREGIONINFO:

       {

              struct region_info_user ur;

 

              if (copy_from_user(      &ur,

                                   (struct region_info_user *)arg,

                                   sizeof(struct region_info_user))) {

                     return -EFAULT;

              }

 

              if (ur.regionindex >= mtd->numeraseregions)

                     return -EINVAL;

              if (copy_to_user((struct mtd_erase_region_info *) arg,

                            &(mtd->eraseregions[ur.regionindex]),

                            sizeof(struct mtd_erase_region_info)))

                     return -EFAULT;

              break;

       }

 

       case MEMGETINFO:

              if (copy_to_user((struct mtd_info *)arg, mtd,

                             sizeof(struct mtd_info_user)))

                     return -EFAULT;

              break;

 

       case MEMERASE:

       {

              struct erase_info *erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);

              if (!erase)

                     ret = -ENOMEM;

              else {

                     wait_queue_head_t waitq;

                     DECLARE_WAITQUEUE(wait, current);

 

                     init_waitqueue_head(&waitq);

 

                     memset (erase,0,sizeof(struct erase_info));

                     if (copy_from_user(&erase->addr, (u_long *)arg,

                                      2 * sizeof(u_long))) {

                            kfree(erase);

                            return -EFAULT;

                     }

                     erase->mtd = mtd;

                     erase->callback = mtd_erase_callback;

                     erase->priv = (unsigned long)&waitq;

                    

                     /*

                       FIXME: Allow INTERRUPTIBLE. Which means

                       not having the wait_queue head on the stack.

                      

                       If the wq_head is on the stack, and we

                       leave because we got interrupted, then the

                       wq_head is no longer there when the

                       callback routine tries to wake us up.

                     */

                     ret = mtd->erase(mtd, erase);

                     if (!ret) {

                            set_current_state(TASK_UNINTERRUPTIBLE);

                            add_wait_queue(&waitq, &wait);

                            if (erase->state != MTD_ERASE_DONE &&

                                erase->state != MTD_ERASE_FAILED)

                                   schedule();

                            remove_wait_queue(&waitq, &wait);

                            set_current_state(TASK_RUNNING);

 

                            ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;

                     }

                     kfree(erase);

              }

              break;

       }

 

       case MEMWRITEOOB:

       {

              struct mtd_oob_buf buf;

              void *databuf;

              ssize_t retlen;

             

              if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))

                     return -EFAULT;

             

              if (buf.length > 0x4096)

                     return -EINVAL;

 

              if (!mtd->write_oob)

                     ret = -EOPNOTSUPP;

              else

                     ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);

 

              if (ret)

                     return ret;

 

              databuf = kmalloc(buf.length, GFP_KERNEL);

              if (!databuf)

                     return -ENOMEM;

             

              if (copy_from_user(databuf, buf.ptr, buf.length)) {

                     kfree(databuf);

                     return -EFAULT;

              }

 

              ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);

 

              if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))

                     ret = -EFAULT;

 

              kfree(databuf);

              break;

 

       }

 

       case MEMREADOOB:

       {

              struct mtd_oob_buf buf;

              void *databuf;

              ssize_t retlen;

 

              if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))

                     return -EFAULT;

             

              if (buf.length > 0x4096)

                     return -EINVAL;

 

              if (!mtd->read_oob)

                     ret = -EOPNOTSUPP;

              else

                     ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);

 

              if (ret)

                     return ret;

 

              databuf = kmalloc(buf.length, GFP_KERNEL);

              if (!databuf)

                     return -ENOMEM;

             

              ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);

 

              if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))

                     ret = -EFAULT;

              else if (retlen && copy_to_user(buf.ptr, databuf, retlen))

                     ret = -EFAULT;

             

              kfree(databuf);

              break;

       }

 

       case MEMLOCK:

       {

              unsigned long adrs[2];

 

              if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))

                     return -EFAULT;

 

              if (!mtd->lock)

                     ret = -EOPNOTSUPP;

              else

                     ret = mtd->lock(mtd, adrs[0], adrs[1]);

              break;

       }

 

       case MEMUNLOCK:

       {

              unsigned long adrs[2];

 

              if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))

                     return -EFAULT;

 

              if (!mtd->unlock)

                     ret = -EOPNOTSUPP;

              else

                     ret = mtd->unlock(mtd, adrs[0], adrs[1]);

              break;

       }

 

             

       default:

              DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);

              ret = -ENOTTY;

       }

 

       return ret;

} /* memory_ioctl */

 

 

 

mtd_fops

MTD字符设备的操作函数结构

static struct file_operations mtd_fops = {                  

       owner:           THIS_MODULE,

       llseek:             mtd_lseek,         /* lseek */

       read:              mtd_read,       /* read */

       write:            mtd_write,     /* write */

       ioctl:              mtd_ioctl,       /* ioctl */

       open:              mtd_open,      /* open */

       release:    mtd_close,      /* release */

};

 

 

 

init_mtdchar

格式:

       static int __init init_mtdchar(void)

注释:

      

功能:

       初始化一个MTD字符设备(设备层)

说明:

       如果定义了CONFIG_DEVFS_FS{

调用devfs_register_chrdev()注册MTD字符设备

调用register_mtd_user()将设备层MTD字符设备的notifier注册进原始设备层

       }

       否则调用register_chrdev()注册MTD字符设备

参数:

      

返回:

       成功:返回0

       注册失败:返回-EAGAIN

调用:

       devfs_register_chrdev()register_chrdev()注册字符设备

被调用:

       __init

       module_init

源代码:

{

#ifdef CONFIG_DEVFS_FS

       if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))

       {

              printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",

                     MTD_CHAR_MAJOR);

              return -EAGAIN;

       }

 

       devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);

 

       register_mtd_user(&notifier);

#else

       if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))

       {

              printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",

                     MTD_CHAR_MAJOR);

              return -EAGAIN;

       }

#endif

 

       return 0;

}

 

cleanup_mtdchar

格式:

       static void __exit cleanup_mtdchar(void)

注释:

      

功能:

       清除MTD字符设备

说明:

       如果定义了CONFIG_DEVFS_FS{

              调用unregister_mtd_user()注销MTD字符设备的notifier,

              调用devfs_unregister_chrdev()注销MTD字符设备

}

否则调用unregister_chrdev()注销MTD字符设备

参数:

      

返回:

      

调用:

       unregister_chrdev()devfs_unregister_chrdev()注销字符设备

被调用:

       __exit

       module_exit

源代码:

{

#ifdef CONFIG_DEVFS_FS

       unregister_mtd_user(&notifier);

       devfs_unregister(devfs_dir_handle);

       devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");

#else

       unregister_chrdev(MTD_CHAR_MAJOR, "mtd");

#endif

}

 

 

/drivers/mtd/chips子目录

/drivers/mtd/chips下文件的主要功能是探测MTD,该目录下文件是chipreg.cgen_probe.ccfi_probe.cjedec_probe.ccfi_cmdset_0001.ccfi_cmdset_0002.cmap_rom.cmap_ram.cmap_absent.camd_flash.cjedec.csharp.c,下面介绍每个文件的功能

为了确定一个Flash是否是一个CFI使能的flash memory器件,首先要往Flash的地址0x55H写入数据0x98H,然后从Flash的地址0x10H处开始连续读取3个存储单元中的内容,如果数据总线返回的3个存储单元的字符分别为'Q','R''Y',那么该器件是一个CFI使能的Flash。在识别FlashCFI使能器件后,通过查询命令来读取CFI查询结构,这些数据的地址和含义在cfi_ident.h文件中。探测CFI接口Flash设备的程序在文件cfi_probe.c中,这些设备的类型为“cfi_probe”。

也可以用JEDEC(电子电器设备联合会)标准设备模仿CFI接口,探测JEDEC设备的程序在jedec_probe.c中,JEDEC设备的类型为“jedec_probe”。

CFI设备和JEDEC设备都要用到g