mmc驱动框架基础介绍

1 篇文章 0 订阅

mmc驱动框架基础介绍

本文主要介绍一下Linux内核的mmc子系统驱动的整体框架。
(作者对SDIO设备不熟悉,所以不过多描述;鄙人才疏学浅,有不当之处,还请指教。)

大概包括以下几个部分:

  • mmc子系统驱动简介
  • mmc设备的识别、初始化流程的大概介绍
  • mmc设备的读写流程大概介绍

mmc子系统驱动简介

mmc子系统驱动分为三层,分别为:

  • Block层:主要作用是对接通用块层,创建块设备及上层请求处理等工作。
  • Core层:主要提供协议层的内容,为Block层、Host层提供相应接口。
  • Host层:主要是对接SOC的MMC控制器,是比较底层的寄存器操作及中断操作。

上下层之间的交互大概如下图所示:
在这里插入图片描述

mmc子系统实际包含三种设备的驱动:

  • mmc设备
  • SD设备
  • SDIO设备

MMC、SD、SDIO的技术本质是一样的(使用相同的总线规范,等等),都是从MMC规范演化而来;MMC强调的是多媒体存储(MM,MultiMedia),SD强调的是安全和数据保护(S,Secure);SDIO是从SD演化出来的,强调的是接口(IO,Input/Output),不再关注另一端的具体形态(可以
是WIFI设备、Bluetooth设备、GPS等等)。

虽然这三种设备共用这一套子系统,但对应到实际的设备时,这三种不同的设备有不同的物理层规范协议:

eMMC:

https://www.jedec.org/

eMMC物理层协议V5.1版本:

JESD84-B51.pdf

SD:

https://www.sdcard.org/

SD物理层协议V3.1版本:

Part_1_Physical_Layer_Simplified_Specification_Ver_3.01.pdf

SDIO:

https://www.sdcard.org/

SDIO物理层协议V2.0版本:

Part_E1_SDIO_Simplified_Specification_Ver2.00.pdf

从下面这个图中也可以看到SDIO其实是SD的扩展:
在这里插入图片描述

或者直接从官网中可以看到更多的关于SD及其扩展的协议:
在这里插入图片描述

mmc设备的初始化、识别流程的大概介绍

这里主要介绍代码中的初始化及识别流程,主要以hi3559av100的代码为例。

初始化流程

在设备启动过程中,会执行sdhci_hisi_init函数注册平台驱动设备,注册完后,系统总线会执行probe进行探测设备,这里的probe函数注册为sdhci_hisi_probe,通过跟代码,可以看到其实这个的调用流程如下所示:
在这里插入图片描述

可以看到这里是初始化了一个延迟工作队列,任务是调用mmc_rescan函数进行识别设备流程。

识别流程

mmc_rescan函数就是用来扫描识别eMMCSDSDIO设备的,设备识别时会一个较低的频率去与设备交互,当设备识别成功后,可以将工作频率提高。这里会先以400KHz~100KHz这四种频率去与设备交互(注1),每次都调用mmc_rescan_try_freq去扫描设备。

注1:虽然代码上会以这四个频率去识别设备,但实际频率往往取决于芯片本身提供的频率。比如如果芯片的MMC控制器模块在初始化阶段只能提供300KHz的频率,那么实际工作就是一直以300KHz的频率去识别设备。

实际扫描时会先尝试识别SDIO设备,如果成功则返回;否则,继续尝试识别SD设备,如果成功则返回;否则,继续尝试识别MMC设备,如果成功则返回;否则返回错误。当然,识别前会先对设备上电,硬件复位等,如果都没有识别到设备,就对设备下电。比如海思有专门的寄存器(PWR_CTRL_R)来控制eMMC或者SD的电源引脚,来控制设备的上下电。
在这里插入图片描述

正常eMMC设备或者SOC芯片上电时已经在卡槽的SD设备,这里已经被识别成功了。而热插拔的设备还需要另外的工作。

热插拔识别流程

这里大概分三个情况来描述热插拔识别流程:

  • SOC带卡检测引脚,且mmc控制器部分带专门的中断寄存器位
  • SOC不带卡检测引脚,用普通中断引脚做检测
  • 不使用卡检测引脚
控制器带专门检测引脚的热插拔识别流程

海思的SOC大部分目前都支持有专门的引脚用来卡插入拔出检测,通过读取PASTATE_R寄存器或者配置中断的方式进行。这里是以中断检测的方式进行卡插入拔出检测:
在这里插入图片描述

可以看到,接收到插入或移除的中断后,中断处理中重新启动detect工作任务去扫描设备。

中断处理的代码如下:

static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
{
	...
	if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
		struct mmc_host *mmc = host->mmc;

		mmc->ops->card_event(mmc);
		mmc_detect_change(mmc, msecs_to_jiffies(200));
	}
	...

	return isr ? IRQ_HANDLED : IRQ_NONE;
}
控制器无专门检测引脚的热插拔识别流程

有的芯片没有专门的检测引脚,需要用普通的GPIO引脚作为检测引脚。这样的话可以在设备树的节点里配置cd的引脚号,并配置触发电平,比如cd-gpios = <&gpio6 27 GPIO_ACTIVE_LOW>。而mmc驱动中会解析此节点的值,并申请gpio中断。

int mmc_of_parse(struct mmc_host *host)
{
    ...
    ret = mmc_gpiod_request_cd(host, "cd", 0, true,
					   0, &cd_gpio_invert); 
    ...
}
无检测引脚的热插拔识别流程

若卡槽没有专门的检测引脚或者SOC上的没有预留检测引脚,可以用轮询的方式来检测设备的热插拔。
在上面的mmc_rescan函数中,可以看到执行到最后有一个条件判断:根据是否有MMC_CAP_NEEDS_POLL能力,来再次调度host->detect任务。而这个能力可以通过设备树节点赋值broken-cd能力获得,或者直接在host初始化时对他的caps能力赋值。

void mmc_rescan(struct work_struct *work)
{
    ...
 out:
	if (host->caps & MMC_CAP_NEEDS_POLL)
		mmc_schedule_delayed_work(&host->detect, HZ);
}

mmc设备的读写流程大概介绍

从开始的子系统简介里面可以看到上下层之间的交互大概流程,无论用户空间是通过文件系统操作mmc设备,都会经过通用块层。这里就对上面的交互流程进行稍微的细化,主要以读写数据为主。

在这里插入图片描述

从上图可以看到,读写数据的流程主要经过以下几层次:(下面的内容也是围绕上面的图来进行)

  • 通用块层
  • mmc block层
  • mmc core层
  • mmc host层
  • soc 控制器 && mmc设备

通用块层

无论是经过文件系统读写,还是直接用open(block_dev)的方式读写mmc设备,最后都是会调用submit_bio函数提交IO请求,最后由内核提交到具体的驱动,这里是mmc驱动:

在这里插入图片描述

如果submit_bio是提交的读操作,那么此时的空间已经由内核分配好;如果提交的是写操作,那么用户空间的数据已经拷贝到内核空间。

图中绿色函数blk_queue_bio,是通用块层提供的接口,最终会调用到红色的函数mmc_request_fn,即到了具体的驱动:mmc驱动层了。

这个mmc_request_fn函数是在mmd驱动的block层初始化时调用通用块层的接口初始化时传入:
在这里插入图片描述

mmc block层

在通用块层调用mmc_request_fn之后,接着便唤醒mmc的读写线程mmcqd/%d%s,线程执行mmc_queue_thread函数,从request_queue请求队列中获取单个请求request,并将请求通过mmc_blk_issue_rq发送出去,最后经过mmc_blk_issue_rw_rq发送到mmc core层。

在这里,会将上层传下来的request请求解析:确定请求的读写方向、单块读写还是多块读写、读写命令的起始地址(在mmc设备上的地址)和数据长度、确定DMA搬移地址、设置超时等工作。
在这里插入图片描述

mmc core层

mmc core层主要工作还是为协议服务。

mmc_start_req函数发起一个非阻塞的请求,此时的请求已经不是上层传下来的request请求,而是一个只在mmc驱动范围内作用的、异步的请求mmc_async_req。如果发送成功这将这次发送的请求返回,否则返回空。
在这里插入图片描述

mmc host层 && soc 控制器 && mmc设备

到了mmc host层,则主要是soc控制器的寄存器相以及中断等操作。在这里,DMA搬移的数据目的地址或者源地址都以及经过dma_map_sg等相关函数映射到scatterlist结构体里,DMA可以直接操作映射后的地址,并且可以避免申请连续大块内存的需求。
在这里插入图片描述

eMMC/SD协议中,发送读写数据时会预先发送命令告诉eMMC/SD设备,命令中会带有将要读写数据的地址,如果设备响应OK,在进行数据交互。读写数据的量可以由两种方式决定,一个是通过CMD23命令并带上要读写的数据量,提前告诉eMMC/SD设备;第二个是通过CMD12命令来直接告诉eMMC/SD设备停止发送/接收数据。

控制器发送/接受完后会产生数据中断,在中断处理函数里,会调用mmc core层的接口,通知上层完成数据的传输。

读写数据返回,通知上层

前面mmc block层提到,mmc_queue_thread函数,从request_queue请求队列中获取单个请求request并发送。如果request为空,但前一个请求没有发送完,这时候会发送一个空的request请求。当数据传输完成后,mmc host层调用mmc core层的mmc_request_done接口,标志数据传输结束,此时函数返回到mmc_blk_issue_rw_rq,并调用通用块层的blk_end_request接口,表示数据以及从设备返回,这时候就代表着一整个读写流程的结束。

读写过程中主要涉及的数据结构

在通用块层到mmc驱动层再到硬件DMA,经过了许多个数据结构:
通用块层使用submit_bio函数提交IO请求时,使用的结构体是struct bio,然后经过blk_queue_bio函数的处理,将bio里面IO请求合并到struct request_queue请求队列里面。到mmc驱动层时,会从request_queue请求队列里面将单个的struct request请求通过blk_fetch_request函数取出,然后request指针会存放到struct mmc_request,同时,在mmc的block层准备数据时,会将request映射到struct scatterlistscatterlist指针存放到struct mmc_data中。在准备DMA传输时,再将scatterlist使用dma_map_sg进行映射,交给DMA传输。

在这里插入图片描述

数据结构的关联关系大概如下图所示,在这里就不过多描述了。
在这里插入图片描述

补充

如何确定读写的位置的

每个struct request都会有一个叫__sector的变量,用来记录读写磁盘的位置的。

struct request同时记录有struct hd_struct *partstruct hd_struct结构体里面也有一个关于位置的变量sector_t start_sect,而这个位置是分区在磁盘上的起始位置。

在这里插入图片描述

mmc设备磁盘数据的加解密过程

在前面数据传输过程的了解以及相关数据结构体的大概关系理清楚后,可以做一个对磁盘数据解密的需求。

大概过程如下:

  1. 打包时将只读分区的数据通过AES算法进行加密,烧到eMMC中。
  2. 设备启动读取只读加密分区时,使用密钥对只读加密分区数据进行解密。

关键点:

  1. 如何判断分区是否需要解密。
  2. 如何正确解密。
  3. 在哪里解密。

做法:

  1. 在上面描述读写位置时,可以知道每一个request都会记录到所属的分区hd_struct,这个结构体中的partition_meta_info里面记录了当前分区的名字,可以根据名字来判断是否需要解密。
  2. AES算法加密分区数据时,是与烧录在磁盘上的位置无关的,如果解密算法把相对位置作为IV去解密,需要将请求的绝对位置换算成相对位置,使用sector_t start_sectrequest的请求位置即可换算出来。
  3. 在mmc驱动层中,只有block层合适做解密。原因有两个:core层提供的data_done之类的函数会在中断上下文执行,解密数据需要时间,不合适;host层没有办法处理发出的重复请求且不知道数据是否有效,直接解密会浪费时间。所在在block层中,在合适的blk_end_request之前执行解密即可。

识别过程的更具体描述

mmc_rescan_try_freq扫描过程就是一个上电->复位->发送命令尝试->成功后按照协议继续发命令的一个流程;如果失败了就执行下电,再次尝试。

在这里插入图片描述

扫描SD设备如下图所示:
在这里插入图片描述

从下面的流程图可以看到,代码就是协议的具体实现:
在这里插入图片描述

SD卡锁定功能的实现

SD卡支持锁定功能,但目前的内核代码是没有支持的,可以通过修改内核代码来实现SD卡锁定功能。

  • 优点:有效保护卡内数据,不输入密码卡无法识别
  • 缺点:需要额外的代码支持,且密码需要在识别过程中输入,交互性不好

更多细节在协议上的4.3.7 Card Lock/Unlock Operation有描述,过程不复杂,但用户密码管理不友好。

当然,还有另外的SD卡安全协议内容:Part_3_Security_Specification_Ver3.00不在本篇讨论范围内。
的实现

SD卡支持锁定功能,但目前的内核代码是没有支持的,可以通过修改内核代码来实现SD卡锁定功能。

  • 优点:有效保护卡内数据,不输入密码卡无法识别
  • 缺点:需要额外的代码支持,且密码需要在识别过程中输入,交互性不好

更多细节在协议上的4.3.7 Card Lock/Unlock Operation有描述,过程不复杂,但用户密码管理不友好。

当然,还有另外的SD卡安全协议内容:Part_3_Security_Specification_Ver3.00不在本篇讨论范围内。

### 回答1: 《Android驱动开发与移植实战详解》是一本系统讲解在Android平台上驱动开发与移植的书籍。本书主要内容包括Android系统架构、驱动框架Linux驱动开发方法、Android下驱动开发与移植等方面。本书由易到难、由浅入深,从讲解Android系统架构和驱动框架开始,通过对Linux内核驱动开发方法的讲解来引入读者进入Android下驱动开发与移植的实践。 本书从Android平台的体系结构开始,系统介绍了Android平台的各个组成部分,包括Java层、Native层和Linux内核层。在介绍完整个Android系统架构的同时,本书也详细讲解了Android内核的驱动框架,包括Linux内核驱动框架和Android内核驱动框架,并详细介绍驱动的编写方法、调试方法和性能优化技巧。 本书还介绍了Android系统下常用硬件的驱动开发方法,包括USB驱动、SPI驱动、I2C驱动MMC/SD卡驱动、LCD驱动、触摸屏驱动、按键驱动等。而且本书还详细介绍了Android系统的设备树,以及如何进行驱动的移植和调试。 总体来说,本书内容丰富,对Android驱动开发及移植有一定的基础和经验的读者可以通过本书进一步提高自己的技术水平。该书能帮助读者深入理解Android系统的内在机制,掌握Android驱动开发与移植的实际应用技能,是一本实用性强的专业书籍。 ### 回答2: 《Android驱动开发与移植实战详解pdf》是一本关于Android系统驱动开发与移植的专业性教材,主要介绍了如何进行Android系统驱动的开发、调试和移植,帮助读者掌握Android系统底层驱动的知识和技术。 本书首先介绍了Android系统的架构和驱动模型,深入分析了Android设备驱动的实现方式和工作流程。然后详细讲解了如何编写和调试Android驱动程序,包括内核模块、字符设备驱动、块设备驱动等多种类型的驱动开发。 此外,本书还解释了Android驱动程序的移植方法和技巧,包括从其他Linux系统移植Android驱动程序、自定义Android驱动程序以及如何把一个驱动程序移植到不同的设备上等重要内容。这样的话,读者可以在实际项目开发中更加容易地完成Android驱动程序的实现和移植。 总之,《Android驱动开发与移植实战详解pdf》是一本涵盖了Android系统驱动开发和移植等方面内容,对于想进一步了解Android系统底层工作原理的读者有着重要的指导意义。无论是初学者还是有经验的开发人员,在阅读本书后都可以获得很多关于Android驱动开发和移植的实用技巧和工具。 ### 回答3: 《android驱动开发与移植实战详解》是一本介绍Android驱动程序开发和移植的实用指南。本书包含了许多基础概念、应用场景和实例,可以帮助读者快速学习Android驱动程序开发和移植的实际应用。 本书的内容涉及很多主题,例如Android系统的架构和驱动模型、设备驱动程序的编写、驱动程序的调试和测试、不同类型设备的驱动程序移植等。其中,编写设备驱动程序需要掌握C语言的基础知识,而调试和测试则需要熟悉一些工具和技术。 本书的每一章节都配有大量的实例代码和案例分析,可以帮助读者深入理解Android驱动程序的开发和移植流程。同时,作者也提供了一些实用的技巧和建议,例如如何选择最合适的驱动程序类型、如何调试设备驱动程序、如何在移植设备驱动程序时避免常见问题等。 总之,《android驱动开发与移植实战详解》是一本非常实用的书籍,可以帮助Android开发者更加深入地了解驱动程序开发和移植的实际应用。对于那些刚开始从事Android开发的读者来说,这本书也是一个非常不错的入门指南。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值