Linux MMC子系统5

Linux MMC子系统5(基于Linux6.6)---MMC driver模块介绍

 


mmc 子系统为何实现了统一的mmc driver,主要有如下几点:

  1. Mmc host已提供了访问mmc card的方法(mmc_host->request);
  2. mmc/emmc/sd等协议规范均已定义mmc card(包括mmc/sd/tf等存储卡)支持的命令格式以及状态流转机制、寄存器定义等信息,因此可使用统一的驱动程序实现对所有厂家的card通信(这是由协议规范规定的,比如针对nandflash驱动模块,也不需要为nandflash设备实现特定驱动,因其协议规范也规定了nandflash需要支持的操作及通信格式等等)

基于以上几点,并不需要针对各厂家的mmc card,实现特殊的mmc driver,因此mmc 子系统实现了统一的mmc driver;

一、相关的数据结构及关联说明

针对mmc driver而言,相关的数据结构包括mmc_driver、device_driver、device、mmc_card、mmc_part、mmc_blk_data、mmc_queue、gendisk、request_queue等;

其中mmc_driver为一个mmc card驱动的抽象;device_driver、device为linux设备驱动模型相关的数据结构(该模块已在前面的文章中介绍,此处不再赘述);gendisk、request_queu、request_fn为块设备模型相关的数据结构(此处不展开);mmc_blk_data、mmc_queue主要用于实现mmc block device的通用块层与mmc host之间进行数据传输;而mmc_card则为mmc_driver所要驱动的外设。

这些数据结构之间的关联如下图所示,它们的关联关系说明如下:

  1. Mmc_driver与mmc_card,通过借助设备-总线-驱动模型的数据结构(device_driver、device)及接口,实现绑定与解绑操作;
  2. 针对mmc card,通过mmc_part可定义该mmc card的分区个数及分区大小等内容(mmc card中extended CSD寄存器中存储分区信息,当通过mmc rescan查找到mmc card时,会通过读取该寄存器的值,获取mmc card的分区信息);
  3. mmc_blk_data用于抽象mmc block device信息,这其中包括mmc_queue、request_queue、gendisk这些数据结构;

在完成mmc_driver与mmc_card的绑定过程中,在调用mmc_driver->probe进行设备探测时,则进行mmc block device的创建,针对每一个分区,均执行如下操作:

  1. 创建mmc_blk_data类型的变量,即为每一个分区均创建对应的通用块设备(gendisk类型);
  2. 创建mmc_queue,并为其创建对应的线程及处理接口(主要用于处理来自块I/O子系统的请求);
  3. 针对对mmc_queue,为其创建request_queue及其请求方法mmc_request_fn,并与gendisk进行关联;
  4. 将gendisk注册至块I/O子系统中,从而将本分区对应的通用块设备注册至块设备对应的bdev_map中,类似于字符设备的cdev_map;

1. MMC Driver 子系统

MMC 驱动子系统是 Linux 内核中专门负责与 MMC 存储设备进行交互的模块。它主要包括以下几个功能:

  • 卡和主机控制器的抽象:处理不同类型的存储设备(如 SD 卡、eMMC 卡、UFS 等)。
  • MMC 核心的命令调度:管理卡的初始化、读写请求和命令的调度。
  • 设备支持:包括插拔检测、卡的电源管理等。

数据结构

  • mmc_driver:这是 MMC 驱动的核心结构体,定义了 MMC 设备与驱动程序的交互接口。例如,它包含了设备初始化、退出、驱动加载等函数。
struct mmc_driver {
    const char *name;             // 驱动名称
    int (*probe)(struct mmc_card *card, struct mmc_host *host);  // 驱动初始化
    void (*remove)(struct mmc_card *card);  // 驱动卸载
    struct device_driver driver;  // 与设备模型的集成
};
  • mmc_card:表示一个插入的 MMC 卡,保存与卡相关的各种属性信息。
struct mmc_card {
    struct mmc_host *host;         // 所属的主机控制器
    unsigned int capacity;         // 存储卡的容量
    struct mmc_device *dev;        // 设备结构体
};

2. 设备-总线-驱动模型(Device-Bus-Driver Model)

Linux 采用设备-总线-驱动模型来实现硬件抽象,这使得设备驱动能够与硬件进行有效的交互,并且简化了设备管理。设备(Device)、总线(Bus)和驱动(Driver)之间通过统一的模型进行连接。

  • 设备模型:设备是一个在 Linux 中表示硬件的抽象体,包含了设备的状态和属性。
  • 总线模型:总线是设备和驱动程序之间的桥梁,负责设备的枚举和驱动的绑定。
  • 驱动模型:驱动程序负责管理和控制设备的操作,确保设备可以正确地与系统交互。

在 MMC 驱动中,mmc_driver 是通过总线模型(如 Platform BusSDHCI 总线)与设备(如 mmc_host)连接的。

数据结构

  • struct device_driver:驱动程序的基类,包含驱动的名称、初始化函数、匹配函数等。
struct device_driver {
    const char *name;             // 驱动名称
    struct bus_type *bus;         // 驱动所属的总线类型
    int (*probe)(struct device *dev);  // 设备匹配并初始化
    int (*remove)(struct device *dev); // 设备移除
};
  • struct device:表示一个设备,包含设备的基本信息和与驱动程序的连接。

3. 块设备驱动模型(Block Device Driver Model)

在 Linux 中,块设备是指按块访问的设备,如硬盘、SSD、SD 卡等。MMC 存储设备也属于块设备的一种。块设备驱动程序负责对这些设备进行读写操作,提供高效的磁盘访问方式。

  • 块设备模型:块设备的驱动程序是通过 block subsystem 提供的接口进行管理和访问的。它管理磁盘分区、文件系统以及对设备的读写操作。

数据结构

  • struct block_device:表示块设备的结构体,它包含设备信息、指向块设备操作的指针等。
struct block_device {
    struct device *bd_device;       // 设备的基础信息
    struct block_operations *bd_disk;  // 设备操作接口
};
  • struct block_operations:包含了对块设备的操作接口,如读、写、打开、关闭等。
struct block_operations {
    int (*open)(struct block_device *bdev);  // 打开设备
    int (*read)(struct block_device *bdev, char *buffer);  // 读取设备
    int (*write)(struct block_device *bdev, const char *buffer);  // 写入设备
};

4. MMC Host 子系统

MMC Host 子系统负责管理与物理硬件的通信,控制 MMC 卡与主机控制器之间的数据流。它提供了主机控制器与卡之间的抽象,并将高层的 MMC 请求转发到硬件层执行。

数据结构

  • mmc_host:表示一个 MMC 主机控制器,包含与硬件交互的必要信息,如支持的操作、最大块大小等。
struct mmc_host {
    struct device *dev;            // 设备的基础信息
    struct mmc_ops *ops;           // 主机控制器的操作函数集
    struct mmc_card *card;         // 当前插入的 MMC 卡
    unsigned int max_blk_size;     // 最大块大小
    unsigned int caps;             // 控制器的能力
};
  • mmc_ops:这是一个包含操作函数的结构体,控制 MMC 主机控制器的行为。
struct mmc_ops {
    int (*init)(struct mmc_host *host);  // 初始化主机控制器
    int (*request)(struct mmc_host *host, struct mmc_request *mrq);  // 请求处理
    int (*send_cmd)(struct mmc_host *host, struct mmc_command *cmd);  // 发送命令
};
  • mmc_request:表示一个 MMC 请求,包含一个命令和数据传输操作。
struct mmc_request {
    struct mmc_command *cmd;        // 发送的命令
    struct mmc_data *data;          // 数据操作
    struct list_head list;          // 队列链表
};

本数据结构体包括了mmc driver 子系统、设备-总线-驱动模型、块设备驱动模型、mmc host子系统这几个模块。针对mmc driver的probe而言,也就是完成如下图的数据结构间的关联。

1.1 mmc_driver 数据结构

/*
 * MMC device driver (e.g., Flash card, I/O card...)
 */
struct mmc_driver {
	struct device_driver drv;
	int (*probe)(struct mmc_card *);
	void (*remove)(struct mmc_card *);
	void (*shutdown)(struct mmc_card *);
};

该数据结构主要包含了device_driver类型的成员,并定义了probe、remove、suspend、resume四个函数指针;这四个函数指针的功能与device_driver中对应的函数指针时对应的。

1.2 struct mmc_card数据结构

该数据结构之前文章已说明。此处不再赘述;

1.3 struct mmc_part数据结构

该数据结构用于描述一个mmc card 分区,主要包括分区大小、分区的类型、分区名称、是否为只读分区等信息。mmccard中extended CSD寄存器中存储分区信息,当通过mmc rescan查找到mmc card时,会通过读取该寄存器的值,获取mmc card的分区信息。

include/linux/mmc/card.h

/*
 * MMC Physical partitions
 */
struct mmc_part {
	u64		size;	/* partition size (in bytes) */
	unsigned int	part_cfg;	/* partition type */
	char	name[MAX_MMC_PART_NAME_LEN];
	bool	force_ro;	/* to make boot parts RO by default */
	unsigned int	area_type;
#define MMC_BLK_DATA_AREA_MAIN	(1<<0)
#define MMC_BLK_DATA_AREA_BOOT	(1<<1)
#define MMC_BLK_DATA_AREA_GP	(1<<2)
#define MMC_BLK_DATA_AREA_RPMB	(1<<3)
};

1.4 struct mmc_blk_data数据结构

该数据结构用于抽象一个mmc card 分区对应的block device的数据信息,其中包括:

  1. 该分区对应的通用磁盘设备gendisk,用于将该分区注册至块设备层,以便应用程序可通过块设备I/O访问该分区;
  2. mmc_queue用于抽象mmc 块设备的请求队列,该数据结构中包括了块设备请求队列成员(request_queue及其访问方法,用于处理块I/O层的数据请求信息,并实现将请求分发给mmc host,进而完成与mmc card的数据通信);
  3. 链表成员part用于将该mmc card所有分区对应的mmc_blk_data变量链接至一起;
  4. force_ro、power_ro_lock主要用于描述该分区对应块设备的设备属性信息。

 drivers/mmc/core/block.c

/*
 * There is one mmc_blk_data per slot.
 */
struct mmc_blk_data {
	struct device	*parent;
	struct gendisk	*disk;
	struct mmc_queue queue;
	struct list_head part;
	struct list_head rpmbs;

	unsigned int	flags;
#define MMC_BLK_CMD23	(1 << 0)	/* Can do SET_BLOCK_COUNT for multiblock */
#define MMC_BLK_REL_WR	(1 << 1)	/* MMC Reliable write support */

	struct kref	kref;
	unsigned int	read_only;
	unsigned int	part_type;
	unsigned int	reset_done;
#define MMC_BLK_READ		BIT(0)
#define MMC_BLK_WRITE		BIT(1)
#define MMC_BLK_DISCARD		BIT(2)
#define MMC_BLK_SECDISCARD	BIT(3)
#define MMC_BLK_CQE_RECOVERY	BIT(4)
#define MMC_BLK_TRIM		BIT(5)

	/*
	 * Only set in main mmc_blk_data associated
	 * with mmc_card with dev_set_drvdata, and keeps
	 * track of the current selected device partition.
	 */
	unsigned int	part_curr;
#define MMC_BLK_PART_INVALID	UINT_MAX	/* Unknown partition active */
	int	area_type;

	/* debugfs files (only in main mmc_blk_data) */
	struct dentry *status_dentry;
	struct dentry *ext_csd_dentry;
};

1.5、struct mmc_queue数据结构

该数据结构用于描述块设备I/O的request请求,该数据结构包含的内容如下:

  1. card用于指向该队列对应的mmc_card;
  2. thread用于描述该mmc_queue对应的线程,该线程主要用于处理所有从块I/O层下发的请求(request_queue);
  3. request_queue类型的变量用于向块I/O层注册request_queue及其请求方法,以便块I/O层可以将上层的数据请求下发至mmc  block层;
  4. issue_fn用于mmc block层将数据请求下发给mmc host层,由mmc host层继续实现数据请求的处理。

drivers/mmc/core/queue.h 

struct mmc_queue {
	struct mmc_card		*card;
	struct mmc_ctx		ctx;
	struct blk_mq_tag_set	tag_set;
	struct mmc_blk_data	*blkdata;
	struct request_queue	*queue;
	spinlock_t		lock;
	int			in_flight[MMC_ISSUE_MAX];
	unsigned int		cqe_busy;
#define MMC_CQE_DCMD_BUSY	BIT(0)
	bool			busy;
	bool			recovery_needed;
	bool			in_recovery;
	bool			rw_wait;
	bool			waiting;
	struct work_struct	recovery_work;
	wait_queue_head_t	wait;
	struct request		*recovery_req;
	struct request		*complete_req;
	struct mutex		complete_lock;
	struct work_struct	complete_work;
};

1.6、Mmc block层的块I/O请求处理

Mmc block层主要借助mmc_queue类型的变量实现块i/o请求,mmc block层提供了块i/o请求处理的线程。块i/o层接口层通过调用mmc block层的request方法(即request_queue->request_fn,即mmc_request_fn接口),该方法通过调用wake_up_process接口,唤醒mmc block层的块i/o处理线程,该线程处理接口则调用mq->issue_fn进行i/o请求的分发,即调用mmc_blk_issue_rq接口,该接口通过一系统的接口调用,最终即调用mmc_host->ops->request,通过mmc_host提供的方法,实现与mmc_card的通信操作。Mmc block层的块i/o处理流程如下所示。

二、mmc driver的probe/remove

上面介绍了mmc driver层相关的数据结构,通过这些数据结构间的关联图,基本上对mmc driver 的probe/remove接口也有了一定的了解,而mmc driver的probe/remove接口,也就是完成上述数据结构间的关联的建立与消除。

2.1 mmc_driver的定义如注册

mmc_driver的定义如下,该driver的名称为“mmcblk”,主要实现了probe/remove接口以及suspend/resume接口(这两个接口主要用于电源管理)。

drivers/mmc/core/block.c

static struct mmc_driver mmc_driver = {
	.drv		= {
		.name	= "mmcblk",
		.pm	= &mmc_blk_pm_ops,
	},
	.probe		= mmc_blk_probe,
	.remove		= mmc_blk_remove,
	.shutdown	= mmc_blk_shutdown,
};

2.2、mmc_driver与mmc_card的绑定流程

mmc_driver与mmc_card借助设备-总线-驱动模型提供的接口完成,mmc_driver与mmc_card绑定,并执行mmc_driver->probe接口。mmc_driver、mmc_card、mmc_bus之间的绑定流程如下所示:

  1. 通过driver_register与device_register,从而完成mmc_driver、mmc_card、mmc_bus之间的绑定。

(当mmc rescan一个mmc card后,则会创建mmc card,并同时调用device_register,将该mmc_card注册至mmc_bus中,并触发__device_attach,从而完成mmc_card与mmc_driver的绑定,并调用mmc_driver->probe,即调用mmc_blk_probe接口,实现mmc block device的创建及注册流程。)

2.2.1、mmc_blk_probe

mmc_blk_probe实现的功能如下:

针对该mmc card的每一个分区,均执行如下操作:

  1. 为每一个分区,创建对应的通用磁盘设备gendisk;
  2. 设置gendisk的fops为mmc_bdops;
  3. 为每一个通用磁盘设备,均创建request_queue及其处理方法;
  4. 为该通用磁盘设备创建mmc block块i/o请求机制;
  5. 将该分区对应的通用磁盘设备注册至块设备i/o中,以便应用层可通过块设备访问该mmc card 分区。

drivers/mmc/core/block.c 

static int mmc_blk_probe(struct mmc_card *card)
{
	struct mmc_blk_data *md;
	int ret = 0;

	/*
	 * Check that the card supports the command class(es) we need.
	 */
	if (!(card->csd.cmdclass & CCC_BLOCK_READ))
		return -ENODEV;

	mmc_fixup_device(card, mmc_blk_fixups);

	card->complete_wq = alloc_workqueue("mmc_complete",
					WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
	if (!card->complete_wq) {
		pr_err("Failed to create mmc completion workqueue");
		return -ENOMEM;
	}

	md = mmc_blk_alloc(card);
	if (IS_ERR(md)) {
		ret = PTR_ERR(md);
		goto out_free;
	}

	ret = mmc_blk_alloc_parts(card, md);
	if (ret)
		goto out;

	/* Add two debugfs entries */
	mmc_blk_add_debugfs(card, md);

	pm_runtime_set_autosuspend_delay(&card->dev, 3000);
	pm_runtime_use_autosuspend(&card->dev);

	/*
	 * Don't enable runtime PM for SD-combo cards here. Leave that
	 * decision to be taken during the SDIO init sequence instead.
	 */
	if (!mmc_card_sd_combo(card)) {
		pm_runtime_set_active(&card->dev);
		pm_runtime_enable(&card->dev);
	}

	return 0;

out:
	mmc_blk_remove_parts(card, md);
	mmc_blk_remove_req(md);
out_free:
	destroy_workqueue(card->complete_wq);
	return ret;
}

 

2.3、mmc_driver与mmc_card的解绑流程

      当mmc card rescan机制,检测到mmc card移除后,在mmc card release的过程中,即调用device_unregiser接口,完成解绑流程,从而触发mmc_blk_remove接口的调用,完成解绑操作。而mmc_blk_remove接口的主要功能如下:

针对每一个mmc card分区,均执行如下操作:

  1. 将该分区对应的mmc 块请求处理机制删除;
  2. 将该分区对应的通用磁盘设备从块设备模块中移除;
  3. 解除mmc_card与mmc_host的关联;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值