linux MTD系统解析,

发布时间:2024-07-19  

MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。

如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。


Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。


MTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c:  MTD原始设备接口相关实现,mtdpart.c :  MTD分区接口相关实现。


MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c :  MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。


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


MTD数据结构:


1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:


 


struct mtd_info *mtd_table[MAX_MTD_DEVICES];  

2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。


 


 


struct mtd_part {  

    struct mtd_info mtd;        /* 分区信息, 大部分由master决定 */  

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

    uint64_t offset;            /* 分区的偏移地址 */  

    int index;                  /* 分区号 (Linux3.0后不存在该字段) */  

    struct list_head list;      /* 将mtd_part链成一个链表mtd_partitons */  

    int registered;  

};  

mtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。


 


 


struct mtd_info {  

    u_char type;         /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */  

    uint32_t flags;      /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */  

    uint64_t size;       /* mtd设备的大小 */  

    uint32_t erasesize;  /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */  

    uint32_t writesize;  /* 写大小, 对于norFlash是字节,对nandFlash为一页 */  

    uint32_t oobsize;    /* OOB字节数 */  

    uint32_t oobavail;   /* 可用的OOB字节数 */  

    unsigned int erasesize_shift;   /* 默认为0,不重要 */  

    unsigned int writesize_shift;   /* 默认为0,不重要 */  

    unsigned int erasesize_mask;    /* 默认为1,不重要 */  

    unsigned int writesize_mask;    /* 默认为1,不重要 */  

    const char *name;               /* 名字,   不重要*/  

    int index;                      /* 索引号,不重要 */  

    int numeraseregions;            /* 通常为1 */  

    struct mtd_erase_region_info *eraseregions; /* 可变擦除区域 */  

      

    void *priv;     /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */  

    struct module *owner;   /* 一般设置为THIS_MODULE */  

      

    /* 擦除函数 */  

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

  

    /* 读写flash函数 */  

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

  

    /* 带oob读写Flash函数 */  

    int (*read_oob) (struct mtd_info *mtd, loff_t from,  

             struct mtd_oob_ops *ops);  

    int (*write_oob) (struct mtd_info *mtd, loff_t to,  

             struct mtd_oob_ops *ops);  

  

    int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);  

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

    int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);  

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

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

    int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);  

  

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

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

    /* Sync */  

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

  

    /* Chip-supported device locking */  

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

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

  

    /* 电源管理函数 */  

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

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

  

    /* 坏块管理函数 */  

    int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);  

    int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);  

  

    void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);  

    unsigned long (*get_unmapped_area) (struct mtd_info *mtd,  

                        unsigned long len,  

                        unsigned long offset,  

                        unsigned long flags);  

    struct backing_dev_info *backing_dev_info;  

    struct notifier_block reboot_notifier;  /* default mode before reboot */  

  

    /* ECC status information */  

    struct mtd_ecc_stats ecc_stats;  

    int subpage_sft;  

    struct device dev;  

    int usecount;  

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

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

};  

mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。


 


如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。


 


int add_mtd_device(struct mtd_info *mtd)  

int del_mtd_device (struct mtd_info *mtd)  

如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。


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

int del_mtd_partitions(struct mtd_info *master)  

其中mtd_partition结构体表示分区的信息


 


 


struct mtd_partition {  

    char *name;             /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */  

    uint64_t size;          /* 分区大小 */  

    uint64_t offset;        /* 分区偏移值 */  

    uint32_t mask_flags;    /* 掩码标识,不重要 */  

    struct nand_ecclayout *ecclayout;   /* OOB布局 */  

    struct mtd_info **mtdp;     /* pointer to store the MTD object */  

};  

其中nand_ecclayout结构体:  

struct nand_ecclayout {  

    __u32 eccbytes;     /* ECC字节数 */  

    __u32 eccpos[64];   /* ECC校验码在OOB区域存放位置 */  

    __u32 oobavail;       

    /* 除了ECC校验码之外可用的OOB字节数 */  

    struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];  

};  

关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。

MTD设备层:


 


mtd字符设备接口:


/drivers/mtd/mtdchar.c文件实现了MTD字符设备接口,通过它,可以直接访问Flash设备,与前面的字符驱动一样,通过file_operations结构体里面的open()、read()、write()、ioctl()可以读写Flash,通过一系列IOCTL 命令可以获取Flash 设备信息、擦除Flash、读写NAND 的OOB、获取OOB layout 及检查NAND 坏块等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL) 


mtd块设备接口:


/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。

MTD硬件驱动层:


Linux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。


MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。


 


struct nand_chip {  

    void  __iomem   *IO_ADDR_R;     /* 读8位I/O线地址 */  

    void  __iomem   *IO_ADDR_W;     /* 写8位I/O线地址 */  

  

    /* 从芯片中读一个字节 */  

    uint8_t (*read_byte)(struct mtd_info *mtd);       

    /* 从芯片中读一个字 */  

    u16     (*read_word)(struct mtd_info *mtd);       

    /* 将缓冲区内容写入芯片 */  

    void    (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);      

    /* 读芯片读取内容至缓冲区/ */  

    void    (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);  

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>