Linux MTD子系统(1):系统层次分析

目录

1. MTD子系统简介

2. MTD子系统的框架分层

3. MTD子系统重要的数据结构

3.1 struct mtd_info

3.2 struct mtd_part

3.3 struct mtd_partition

3.4 struct nand_chip

4. MTD相关层之间的联系


1. MTD子系统简介

MTD(memory technology device),即内存技术设备,是Linux针对flash设备(nor flash、nand flash等)设计的标准化硬件驱动框架,MTD为了使flash设备的驱动更加简单,因此在上层和硬件之间提供了一个抽象的接口,这样可以在是用不同的flash设备时可以使用相同的API;MTD所有源码位于/drivers/mtd目录下。

2. MTD子系统的框架分层

MTD子系统的框架分层如上图所示,从上到下主要分为四层,分别为设备节点层、MTD设备层、MTD原始设备层以及flash硬件驱动层

设备节点层:可以通过mknod命令在/dev目录下创建MTD设备节点,其中MTD字符设备节点的主设备号为90,MTD块设备节点的主设备号为31,这样就可以通过创建出来的设备节点来访问MTD字符设备和块设备。

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

MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码,另一部分是各个特定Flash的数据,比如分区信息;用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数;原始设备层中重要的文件有两个:一个是mtdcore.c(MTD原始设备接口相关实现 ),另一个是mtdpart.c(MTD分区接口相关实现)。

flash硬件驱动层:flash硬件驱动层的主要作用是初始化flash硬件、对flash设备进行擦除、读、写的具体操作,以及建立从具体的flash设备与MTD原始设备的映射关系;spi nor flash通用驱动位于drivers/mtd/spi-nor/目录下,CFI/jedec通用驱动位于rivers/mtd/chips/目录下,nand flash通用驱动位于drivers/mtd/nand/目录下,nor flash映射关系相关函数位于drivers/mtd/maps/目录下。

3. MTD子系统重要的数据结构

3.1 struct mtd_info

mtd_info结构体用于表示MTD原始设备,它定义了大量的关于MTD的数据和操作函数。

struct mtd_info {
	u_char type; // MTD类型
	uint32_t flags; // MTD属性标志
	uint64_t size;	 // MTD设备的总大小
	uint32_t erasesize; // 擦除单元的大小,对于Nand就是块的大小
	uint32_t writesize; // 写大小,nor是一个字节,nand是一个页,需确保writesize为1或更大
	uint32_t writebufsize;

	uint32_t oobsize;   // 每个块的OOB数据量(例如 16)
	uint32_t oobavail;  // 每个块的可用OOB字节数

    /* 默认为0 */
	unsigned int erasesize_shift;
	unsigned int writesize_shift;

	/* 基于erasesize_shift和writesize_shift的掩码,默认为1 */
	unsigned int erasesize_mask;
	unsigned int writesize_mask;

	unsigned int bitflip_threshold;

	// Kernel-only stuff starts here.
	const char *name; // 名字
	int index; // 索引

	/* OOB布局说明 */
	const struct mtd_ooblayout_ops *ooblayout;

	/* NAND配对方案,仅提供给MLC/TLC NAND */
	const struct mtd_pairing_scheme *pairing;

	/* ecc步长 */
	unsigned int ecc_step_size;

	/* 每个ecc步长的最大可纠正位错误数 */
	unsigned int ecc_strength;

	/* 可变擦除区域的数据,通常为1 */
	int numeraseregions;
	struct mtd_erase_region_info *eraseregions;

	/*
	 * Do not call via these pointers, use corresponding mtd_*()
	 * wrappers instead.
	 */
    /* 擦除flash函数 */
	int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
	int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
		       size_t *retlen, void **virt, resource_size_t *phys);
	int (*_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);

    /* 读写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);
	int (*_panic_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, size_t len,
				    size_t *retlen, struct otp_info *buf);
	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, size_t len,
				    size_t *retlen, struct otp_info *buf);
	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 to,
				     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);
	void (*_sync) (struct mtd_info *mtd);
	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 (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);

    /* 坏块管理函数 */
	int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);
	int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
	int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
	int (*_max_bad_blocks) (struct mtd_info *mtd, loff_t ofs, size_t len);

    /* 电源管理函数 */
	int (*_suspend) (struct mtd_info *mtd);
	void (*_resume) (struct mtd_info *mtd);

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

	int (*_get_device) (struct mtd_info *mtd);
	void (*_put_device) (struct mtd_info *mtd);

	struct notifier_block reboot_notifier;  /* 重启前的默认模式 */

	/* ECC状态信息 */
	struct mtd_ecc_stats ecc_stats;

	int subpage_sft;

	void *priv;

	struct module *owner;
	struct device dev;
	int usecount;
	struct mtd_debug_info dbg;
};

3.2 struct mtd_part

mtd_part结构体用于表示MTD分区,其中包含了 mtd_info结构体用于描述该分区,每一个分区都是被看成一个MTD 原始设备。

struct mtd_part {
	struct mtd_info mtd; // 分区的详细信息
	struct mtd_info *parent; // 闪存设备或其他分区
	uint64_t offset; // 分区的偏移量
	struct list_head list; // 将mtd_part链成一个链表mtd_partitons
};

3.3 struct mtd_partition

mtd_partition结构体表示分区的信息。

struct mtd_partition {
	const char *name;		/* 分区名 */
	const char *const *types;	/* 要使用的解析器的名称(如果有) */
	uint64_t size;			/* 分区大小 */
	uint64_t offset;		/* 分区偏移值 */
	uint32_t mask_flags;		/* 掩码标识 */
	struct device_node *of_node;
};

3.4 struct nand_chip

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

struct nand_chip {
	struct mtd_info mtd;
	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);
    /* 在低8条I/O线上向芯片写入一个字节 */
	void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
    /* 将缓冲区内容写入芯片 */
	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);
    /* 选中芯片 */
	void (*select_chip)(struct mtd_info *mtd, int chip);
    /* 使用OOB标记检查是否是坏块 */
	int (*block_bad)(struct mtd_info *mtd, loff_t ofs);
    /* 标记坏块 */
	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
    /* 命令、地址、数据控制函数 */
	void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
    /* 设备是否就绪 */
	int (*dev_ready)(struct mtd_info *mtd);
    /* 实现命令发送 */
	void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
			int page_addr);
	int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    /* 擦除函数 */
	int (*erase)(struct mtd_info *mtd, int page);
    /* 扫描坏块表函数 */
	int (*scan_bbt)(struct mtd_info *mtd);
	int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
			int feature_addr, uint8_t *subfeature_para);
	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
			int feature_addr, uint8_t *subfeature_para);
	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
	int (*setup_data_interface)(struct mtd_info *mtd, int chipnr,
				    const struct nand_data_interface *conf);

	int chip_delay;
	unsigned int options;
	unsigned int bbt_options;

	int page_shift;
	int phys_erase_shift;
	int bbt_erase_shift;
	int chip_shift;
	int numchips;
	uint64_t chipsize;
	int pagemask;
	int pagebuf;
	unsigned int pagebuf_bitflips;
	int subpagesize;
	uint8_t bits_per_cell;
	uint16_t ecc_strength_ds;
	uint16_t ecc_step_ds;
	int onfi_timing_mode_default;
	int badblockpos;
	int badblockbits;

	struct nand_id id;
	int onfi_version;
	int jedec_version;
	union {
		struct nand_onfi_params	onfi_params;
		struct nand_jedec_params jedec_params;
	};
	u16 max_bb_per_die;
	u32 blocks_per_die;

	struct nand_data_interface *data_interface;

	int read_retries;

	flstate_t state;

	uint8_t *oob_poi;
	struct nand_hw_control *controller;

	struct nand_ecc_ctrl ecc; /* ECC控制结构 */
	struct nand_buffers *buffers;
	unsigned long buf_align;
	struct nand_hw_control hwcontrol;

	uint8_t *bbt; /* 坏块表指针 */
	struct nand_bbt_descr *bbt_td; /* 用于闪存查找的坏块表描述符 */
	struct nand_bbt_descr *bbt_md; /* 坏块表镜像描述符 */
    
	struct nand_bbt_descr *badblock_pattern; /* 用于初始坏块扫描的坏块扫描模式 */
   
	void *priv; /* 指向私有芯片数据的指针 */

    /* 包含制造商信息 */
	struct {
		const struct nand_manufacturer *desc;
		void *priv;
	} manufacturer;
};

4. MTD相关层之间的联系

​​​​​​​

5. 参考文章

Linux MTD系统剖析_lwj103862095的博客-CSDN博客

​​​​​​​Linux内核4.14版本——mtd子系统(1)——简介_风雨兼程8023的博客-CSDN博客_mtd子系统


版权声明:本文为MisakiMe1原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。