Linux MTD子系统学习(一)

2024-02-09 03:50
文章标签 linux 学习 子系统 mtd

本文主要是介绍Linux MTD子系统学习(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 Linux MTD基本概述
1.1 专有名词描述
1. MTD:Memory Technology Device,内存技术设备。
2. JEDEC:Joint Electron Device Engineering Council,电子电器设备联合会。
3. CFI:Common Flash Interface,通用Flash接口,Intel发起的一个Flash的接口标准。
4. OOB: out of band,某些内存技术支持out-of-band数据——例如,NAND flash每512字节的块有16个字节的extra
data,用于纠错或元数据。
5. ECC: error correction,某些硬件不仅允许对flash的访问,也有ecc功能,所有flash器件都受位交换现象的困扰。在某些情况下,一个比特位会发生反转或被报告反转了,如果此位真的反转了,就要采用ECC算法。
6. erasesize: 一个erase命令可以擦除的最小块的尺寸。
7. buswidth:MTD设备的接口总线宽度。
8.interleave:交错数,几块芯片平行连接成一块芯片,使buswidth变大。
9. devicetype:芯片类型,x8、x16或者x32。
10.NAND:一种Flash技术,参看NAND和NOR的比较。
11.NOR:一种Flash技术,参看NAND和NOR的比较。
1.2 MTD介绍
MTD(memory technology device):内存技术设备,是linux用于描述ROM,NAND,NOR等设备的子系统的抽象,MTD设备可以按块读写也可以按字节读写,也就是说MTD设备既可以是块设备也可以是字符设备,块设备(mtdblackx)操作针对文件系统,字符设备(mtdx)操作主要针对格式化等操作的测试用。
由于块设备的I/O性能与CPU相比很差,因此,块设备的数据流往往会引入文件系统的cache机制
注意:MTD设备既非块设备也不是字符设备,但可以同时提供字符设备和块设备接口来操作。
在这里插入图片描述

如上图所示,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字符设备和块设备。

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

//源码:include/linux/mtd/mtd.h

struct mtd_info {
u_char type; //mtd类型,如:MTD_NORFLASH(See:mtd-abi.h)
uint32_t flags;// 标志位, MTD_WRITEABLE、MTD_NO_ERASE等(See mtd-abi.h)
uint64_t size; // Total size of the MTD(mtd设备的大小)

/* "Major" erase size for the device. Na茂ve users may take this* to be the only erase size available, or may use the more detailed* information below if they desire*/
uint32_t erasesize;//擦除大小,即flash的块大小(mtd设备可能有多个erasesize)
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even* though individual bits can be cleared), in case of NAND flash it is* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR* it is of ECC block size, etc. It is illegal to have writesize = 0.* Any driver registering a struct mtd_info must ensure a writesize of* 1 or larger.*/
uint32_t writesize;// 写大小, 对于norFlash是字节,对nandFlash为一页/** Size of the write buffer used by the MTD. MTD devices having a write* buffer can write multiple writesize chunks at a time. E.g. while* writing 4 * writesize bytes to a device with 2 * writesize bytes* buffer the MTD driver can (but doesn't have to) do 2 writesize* operations, but not 4. Currently, all NANDs have writebufsize* equivalent to writesize (NAND page size). Some NOR flashes do have* writebufsize greater than writesize.*/
uint32_t writebufsize;uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
uint32_t oobavail;  // Available OOB bytes per block/** If erasesize is a power of 2 then the shift is stored in* erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.*/
unsigned int erasesize_shift;	//默认为0,不重要
unsigned int writesize_shift;	//默认为0,不重要
/* Masks based on erasesize_shift and writesize_shift */
unsigned int erasesize_mask;	//默认为1,不重要
unsigned int writesize_mask;	//默认为1,不重要/** read ops return -EUCLEAN if max number of bitflips corrected on any* one region comprising an ecc step equals or exceeds this value.* Settable by driver, else defaults to ecc_strength.  User can override* in sysfs.  N.B. The meaning of the -EUCLEAN return code has changed;* see Documentation/ABI/testing/sysfs-class-mtd for more detail.*/
unsigned int bitflip_threshold;// Kernel-only stuff starts here.
const char *name;	//mtd设备名
int index;/* OOB layout description */
//nand_ecclayout结构体指针, 表示的是ecc布局,可参考硬件手册的OOB中ecc布局
const struct mtd_ooblayout_ops *ooblayout;/* NAND pairing scheme, only provided for MLC/TLC NANDs */
const struct mtd_pairing_scheme *pairing;/* the ecc step size. */
unsigned int ecc_step_size;/* max number of correctible bit errors per ecc step */
unsigned int ecc_strength;/* Data for variable erase regions. If numeraseregions is zero,* it means that the whole device has erasesize as given above.*/
int numeraseregions;//通常为1
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);//重启函数
/** If the driver is something smart, like UBI, it may need to maintain* its own reference counting. The below functions are only for driver.*/
int (*_get_device) (struct mtd_info *mtd);
void (*_put_device) (struct mtd_info *mtd);struct notifier_block reboot_notifier;  /* default mode before reboot *//* ECC status information */
struct mtd_ecc_stats ecc_stats;//ECC状态
/* Subpage shift (NAND) */
int subpage_sft;//私有数据, cfi接口flash指向map_info结构, 或指向自定义flash相关结构体
void *priv; 
struct module *owner;
struct device dev;
int usecount;
struct mtd_debug_info dbg;

};

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

//源码:drivers/mtd/mtdpart.c

struct mtd_part {
struct mtd_info mtd; //分区信息,大部分来自parent
struct mtd_info *parent;//分区的主分区
uint64_t offset; //分区的偏移地址
struct list_head list; //链接到mtd_partitions链表中
};

2.3 mtd_partition
该结构体表示某一分区的分区信息,其将被添加到mtd_partitions链表中。
//源码:include/linux/mtd/patitions.h

struct mtd_partition {
const char name; / identifier string */
const char *const types; / names of parsers to use if any /
uint64_t size; /
partition size /
uint64_t offset; /
offset within the master MTD space /
uint32_t mask_flags; /
master MTD flags to mask out for this partition */
struct device_node *of_node;
};
2.4 mao_info
//源码:include/linux/mtd/map.h

struct map_info {
const char *name; //名称
unsigned long size; //大小
resource_size_t phys; //物理地址
#define NO_XIP (-1UL)
void __iomem *virt; //虚拟地址,通常通过ioremap将物理地址映射得到的
void *cached;

int swap; /* this mapping's byte-swapping requirement */
int bankwidth; /* in octets. This isn't necessarily the widthof actual bus cycles -- it's the repeat intervalin bytes, before you are talking to the first chip again.*/
//读写函数
#ifdef CONFIG_MTD_COMPLEX_MAPPINGSmap_word (*read)(struct map_info *, unsigned long);void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
void (*write)(struct map_info *, const map_word, unsigned long);
void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);/* We can perhaps put in 'point' and 'unpoint' methods, if we reallywant to enable XIP for non-linear mappings. Not yet though. */

#endif
/* It’s possible for the map driver to use cached memory in its
copy_from implementation (and only with copy_from). However,
when the chip driver knows some flash area has changed contents,
it will signal it to the map driver through this routine to let
the map driver invalidate the corresponding cache as needed.
If there is no cache to care about this can be set to NULL. */
void (*inval_cache)(struct map_info *, unsigned long, ssize_t);

/* This will be called with 1 as parameter when the first map user* needs VPP, and called with 0 when the last user exits. The map* core maintains a reference counter, and assumes that VPP is a* global resource applying to all mapped flash chips on the system.*/
void (*set_vpp)(struct map_info *, int);unsigned long pfow_base;
unsigned long map_priv_1;	//驱动可用的私有数据
unsigned long map_priv_2;
struct device_node *device_node;
void *fldrv_priv;
struct mtd_chip_driver *fldrv;

};

2.5 nand_chip
源码:include/linux/mtd/nand.h

struct nand_chip {
void __iomem *IO_ADDR_R; //读写8根io线的地址
void __iomem *IO_ADDR_W;

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);
//验证芯片写入缓冲区的数据
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);//片选芯片
void (*select_chip)(struct mtd_info *mtd, int chip);
//检测是否有坏块
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
//坏块标记
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
//命令、地址、数据控制函数
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,u8 *id_data);
//检查设备是否就绪
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);
//擦除命令处理
void (*erase_cmd)(struct mtd_info *mtd, int page);
//扫描坏块
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,int status, int page);
//写一页
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);int chip_delay;		//有板决定延迟时间
unsigned int options;
unsigned int bbt_options;int page_shift;	//表示page的大小,如Nand的页大小为512byte,则page_shift为9
int phys_erase_shift;	//擦除块的大小

/* 用位表示的bad block table的大小,通常一个bbt占用一个block,
所 以bbt_erase_shift通常与phys_erase_shift相等 */
int bbt_erase_shift;
int chip_shift; //芯片容量
int numchips; //芯片数量
uint64_t chipsize; //芯片大小
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;
int badblockbits;

int onfi_version;
struct nand_onfi_params	onfi_params;flstate_t state;uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;	//ECC布局struct nand_ecc_ctrl ecc;			//ECC校验结构体,含有大量ECC校验的函数
struct nand_buffers *buffers;
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;

};

2.6 spi_nor
用于表示一个spi-nor设备的相关信息。

struct spi_nor {
struct mtd_info mtd; //nor mtd信息
struct mutex lock;
struct device *dev;
u32 page_size; //flash的页大小
u8 addr_width; //地址宽度
u8 erase_opcode; //flash擦除指令
u8 read_opcode; //flash 读数据指令
u8 read_dummy; //读操作时的dummy数据
u8 program_opcode; //页操作指令
enum spi_nor_protocol read_proto;//多线读写时的相关操作
enum spi_nor_protocol write_proto;
enum spi_nor_protocol reg_proto;
bool sst_write_second;

//标志位,如:SNOR_F_USE_FSR(See:spi-nor.h,spi_nor_option_flags)
u32			flags;
u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];//为读、写、擦除、lock、unlock的相关准备工作
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);//读nor内部寄存器
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
//写nor内部寄存器
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);//读nor内部数据
ssize_t (*read)(struct spi_nor *nor, loff_t from,size_t len, u_char *read_buf);
//往nor写入数据
ssize_t (*write)(struct spi_nor *nor, loff_t to,size_t len, const u_char *write_buf);
//擦除flash数据
int (*erase)(struct spi_nor *nor, loff_t offs);//锁住flash的部分区域
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);void *priv;//私有数据

};

注:以上源码基于Linux-4.14.14分析。
————————————————
版权声明:本文为CSDN博主「楓潇潇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013836909/article/details/93298658

这篇关于Linux MTD子系统学习(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/693027

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全

linux安装、更新、卸载anaconda实践

《linux安装、更新、卸载anaconda实践》Anaconda是基于conda的科学计算环境,集成1400+包及依赖,安装需下载脚本、接受协议、设置路径、配置环境变量,更新与卸载通过conda命令... 目录随意找一个目录下载安装脚本检查许可证协议,ENTER就可以安装完毕之后激活anaconda安装更