目录
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子系统
|