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使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

什么是 Linux Mint? 适合初学者体验的桌面操作系统

《什么是LinuxMint?适合初学者体验的桌面操作系统》今天带你全面了解LinuxMint,包括它的历史、功能、版本以及独特亮点,话不多说,马上开始吧... linux Mint 是一款基于 Ubuntu 和 Debian 的知名发行版,它的用户体验非常友好,深受广大 Linux 爱好者和日常用户的青睐,

Linux(Centos7)安装Mysql/Redis/MinIO方式

《Linux(Centos7)安装Mysql/Redis/MinIO方式》文章总结:介绍了如何安装MySQL和Redis,以及如何配置它们为开机自启,还详细讲解了如何安装MinIO,包括配置Syste... 目录安装mysql安装Redis安装MinIO总结安装Mysql安装Redis搜索Red

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

Linux磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig