Android平台_驱动_SD_软件 概要设计说明书

1.引言

1.1 编写目的

  手机项目中描述SD部分的驱动设计架构、设计方法。为上层应用提供底层的函数接口和功能,方便上层对SD应用的正确调用,作为开发和测试人员制定测试规范的参考文档。主要读者适用于手机项目组驱动开发人员,软件代表,项目经理、测试等相关人员,供其他项目组驱动人员参考。

 

1.2缩略语

缩略语

英文全名

中文解释

SD

Secure digital memory card

安全数据媒体卡

eMMC

embedded MultiMedia Card

嵌入式多媒体卡

SDIO

Secure Digital Input and Output Card

 安全数字输入输出卡

SDCC

secure digital card controller

安全数字卡控制器

SDHCI

Secure Digital Host Controller Interface

安全数字主控制器接口

JEDEC

Joint Electron Device Engineering Council

电子工程设计发展联合会议

JESD79-3E

JEDEC DDR

DDR3 协议标准

JESD84-B51

JEDEC SD

eMMC5.1 协议标准

vold

Volume Daemon

存储器守护进程

BIO

block I/O

块设备IO;

GPIO

general purpose input/output

通用输入输出口

DMA

Direct memory access

直接内存存取

FDE

Full Disk Encryption

全盘加密

SDHCI

Secure Digital Host Controller Interface

安全数字控制器接口

QTI

Qualcomm Technologies, Inc.

高通技术公司

ADMA

Advanced DMA

高端直接内存访问

                                             

1.3 参考文档

1, Linux 设备驱动开发.pdf

2, 精通Linux设备驱动程序开发

3, 高通平台项目SD卡需求规格说明书

4, 80_NV610_64_MSM8952_MSM8956_MSM8976_Storage_Overview.pdf

5, 80-NL199-1_SDHCIARCHITECTURE AND DEBUGGING.pdf

6, memorycontroller 控制器的配置;

 网页资料:

博客文档参考:

块设备:

基于块设备驱动;

Linux设备驱动--块设备(一)之概念和框架 

http://blog.csdn.net/jianchi88/article/details/7212370

Linux设备驱动--块设备(二)之相关结构体

http://blog.csdn.net/jianchi88/article/details/7212599

Linux设备驱动--块设备(三)之程序设计

http://blog.csdn.net/jianchi88/article/details/7212701

 

Linux设备驱动--块设备(四)之“自造请求”

http://blog.csdn.net/jianchi88/article/details/7213290

Linux内核之mmc子系统-sdio      

http://blog.csdn.net/mrwangwang/article/details/35997153/

 ARM Linux 3.x的设备树(Device Tree)   http://blog.csdn.net/21cnbao/article/details/8457546

 linux device tree源代码解析 http://blog.chinaunix.net/uid-27717694-id-4274992.html

 深入理解Android之设备加密DeviceEncryption       http://blog.csdn.net/innost/article/details/44519775

 2 开发环境

模块单元

规格

备注

处理器(MCU)

MSM8953

 

内核版本

Linux3.1.8

 

Android版本

7.0

 

编译环境

Ubuntu

 

交叉编译环境

ARM Compiler Tools 5.01 update 3

 

调试和测试工具

ADBTRACE32,串口,dump日志

 

 

 

 

3 需求分析

3.1 存储器分类

  SLCMLCTLC三种闪存的MOSFET是完全一样的,区别如下:

1)如何对单元进行编程:区别在于每个存储单元存储的数据位数;

SLC要么编程,要么不编程,状态只能是01

MLC每个单元存储俩比特,状态就有四种00011011,电压状态对应也有四种。

TLC每个单元三个比特,状态就有八种了(000001010100011101110111),电压状态就有八种。

2)性价比:

SLC=Single-LevelCell,即1bit/cell,速度快寿命长,价格超贵(约MLC3倍以上的价格),约10万次擦写寿命 
MLC=Multi-LevelCell
,即2bit/cell,速度一般寿命一般,价格一般,约3000---10000次擦写寿命 
TLC=Trinary-LevelCell
,即3bit/cell,也有Flash厂家叫8LC,速度相对慢寿命相对短,价格便宜,约500次擦写寿命

    3)应用:

    简单地说SLC的性能最优,价格超高。一般用作企业级或高端发烧友。MLC性能够用,价格适中为消费级SSD应用主流,TLC综合性能最低,价格最便宜。但可以通过高性能主控、主控算法来弥补、提高TLC闪存的性能。

    在手机闪存中,常常使用的MLC

    为了保存重要的数据,采用增强型用户分区;将MLC重新编程为SLC,在牺牲一半容量低前提下,保证重要数据可以稳定的保存;

    4)简单做一个表述;


3.2 EMMC/SD卡协议概要:

EMMC协议针对不同的平台和时间进行的演化示意图如下:

 


Android平台_驱动_SD_软件 概要设计说明书_第1张图片


MSM8952MSM8953,都是使用了EMMC5.1

 

如下图,EMC协议仅仅限于SDIO接口与Device Controller之前的通信;

 Android平台_驱动_SD_软件 概要设计说明书_第2张图片Android平台_驱动_SD_软件 概要设计说明书_第3张图片


通信协议分为三部分:command, data, response;

 

command和response,呈现在CMD线;

data,呈现在1bit, 4bit, 8bit的DATA线上;

 

Android平台_驱动_SD_软件 概要设计说明书_第4张图片

 

 

 

读写,通过command触发,并会不断受到response;

以块为最小的单位,block+CRC校验作为一个传输单位;

       command全部有host触发,有不同class的各级命令;

response由device被动的响应;针对不同的command命令,有不同的response;

 

Android平台_驱动_SD_软件 概要设计说明书_第5张图片

 

擦除的方式:EraseTrim Discard Sanitize详解 http://www.xuebuyuan.com/2200006.html

 

3.4 SD卡协议

SD1.0,2.0,3.0;

目前8953平台支持SD3.0协议;

具体协议参考:sd3.0协议_SD卡协议.pdf

 SD card协议不断演进,目前已经支持到SD3.0,主要是速度和容量的变化,差别如图:

协议

最大容量

理论最大速度

SD1.0

2GB

25 MB/S

SD1.1

2GB

50 MB/S

SD2.0

32GB

50 MB/S

SD3.0

2TB

208 MB/S

 

 3.5 分区

常见的分区包括:物理分区和逻辑分区;

物理分区:eMMC本身自己的分区,即物理上的,不是通过软件就能实现的分区。

逻辑分区:Android 2.x.x 版本上使用的是MBR,4.0版本以后就是使用的GPT分区方式。

不管是MBR还是GPT,他们的分区都是指“逻辑上”的!!!即通过软件实现的,文件系统级别的。

 

EMMC的分区配置;http://blog.sina.com.cn/s/blog_71fdf1f00102v5c2.html

 

EMMC的分区有一些是AP不能修改的(如BOOT1、BOOT2和RPMB分区),有一些是可以通过特定的命令和寄存器就可以修改的(如Enhanced Partition和GPAP)。下面就来集体说明一下:

      通常,从厂家出来的eMMC 主要由这几个部分组成:

1.    BOOT Area Partition 1

2.    BOOT Area Partition 2

3.    RPMB

4.    General purpose Partitions

5.    Enhanced user data area

Android平台_驱动_SD_软件 概要设计说明书_第6张图片

Enhanced Partition的主要功能就是将MLC配置成为SLC;将单个存储单元的存储信息由2位变成1位,在牺牲一半空间的基础上,获取稳定的特性,为保存重要的数据提供便利;

 

 

3.6 功能需求(包括用户,开发,业务,产线测试,研发调试等功能)

SD卡是Secure digitalmemory card的缩写,因其具有高安全性,高容量,高性能,体积小,低功耗,协议简单等多方面的优势,已经被广泛应用于各个领域。因SD主要功能是用于资料的存储,能正常存储是SD卡应用的主要功能。其次是达到用户对于性能的要求,比如读取速率,长时间读取卡的可靠性,不支持热插拨,突然掉电可靠性等。

1, emmc,烧录保存系统文件,保证系统启动;

2, 传输和存储用户数据;

       性能需求

 3.7 性能需求

   SD卡功能主要进行数据存储功能,具体性能包括:

1:能为系统应用提供存储接口

2:长时间数据传输稳定,数据没有错误

3:能支持各种厂商,不同容量的SD卡,兼容性高(1G\2G\4G\8G\16G\\32G\64G\2T)

4:休眠唤醒后,SD卡能正常使用

5:支持SD卡当U盘使用(写速度不小于2MB/S,读速度不小于3MB/S  )

6读写速度符合要求(写速度不小于2MB/S,读速度不小于8MB/S)

7:插入SD卡后,读写时工作电流和待机电流满足datasheet要求

8:SD文件系统可读写,文件系统不会损坏

3.8 风险分析

序号

内容

1

可靠性,传输数据的稳定性

2

兼容性

3

对多次热插拨的支持

4

读写速度保证

 4 硬件原理

4.1 原理图分析

EMMC接口:

(CPU端)

Android平台_驱动_SD_软件 概要设计说明书_第7张图片

 

EMMC端的电源:

Android平台_驱动_SD_软件 概要设计说明书_第8张图片

 

SD卡电路:

(CPU端)

 Android平台_驱动_SD_软件 概要设计说明书_第9张图片

(卡槽接口端)

 Android平台_驱动_SD_软件 概要设计说明书_第10张图片

 

SD卡检测管脚:(gpio_67)

Android平台_驱动_SD_软件 概要设计说明书_第11张图片

6:初始化时上电流程:首先VDD上电,然后VDD_io上电,启动完后,如果中间没有对SD操作,动态将VDD电源调为0V,当有对SD卡读写时,重新对SD卡上电,VDD_io电压根据不同协议的卡去调整电压。

 

 Android平台_驱动_SD_软件 概要设计说明书_第12张图片

上电时序:(EMMC协议的P257页)

 

SD卡协议,P122;

Android平台_驱动_SD_软件 概要设计说明书_第13张图片

7:在OTA升级时,首先卸载掉SD卡或是关掉SD卡电源,避免在升级过程中对SD卡操作,待升级完后,再重新上电,然后挂载SD卡

8:恢复出厂设置时,首先关掉SD卡电源,避免在升级过程中对SD卡写参数,导致SD卡文件损坏或是烧卡,在恢复出厂设置后,再重新上电,然后挂载SD卡

9:fastmmi模式下,上电SD,并初始化,并挂载;


SD 与CPU 的通信协议使用SDIO 方式进行通信。

4.2 模块硬件资源

4.24.4      模块硬件资源


需要硬件资源

GPIO网络名字

功能描述

信号方向

(相对CPU而言)

开机时默认配置

On  off 配置

  备注

SDC2_CLK

时钟线

CPU 到 SD

VDD_P2

没有上拉

 

SDC2_CMD

命令线

双向

VDD_P2

(on)输出上拉   (off)输出上拉

 

SDC2_DATA_0

数据线

双向

VDD_P2

(on)输出上拉   (off)输出上拉

 

SDC2_DATA_1

数据线

双向

VDD_P2

(on)输出上拉   (off) 输出上拉

 

SDC2_DATA_2

数据线

双向

VDD_P2

(on)输出上拉   (off) 输出上拉

 

SDC2_DATA_3

数据线

双向

VDD_P2

(on)输出上拉  (off) 输出上拉

 

 

GPIO资源表

       Android平台_驱动_SD_软件 概要设计说明书_第14张图片      

 在MSM8952.dtsi里定义:

    sdhc_1: sdhci@7824000 {

        compatible = "qcom,sdhci-msm";

        reg = <0x7824900 0x500>,<0x7824000 0x800>, <0x7824E00 0x200>;

        reg-names = "hc_mem","core_mem", "cmdq_mem";

         interrupts = <0 123 0>, <0 1380>;

        interrupt-names = "hc_irq","pwr_irq";

 

        sdhc-msm-crypto =<&sdcc1_ice>;

        qcom,bus-width = <8>;

 

        qcom,qos-planes = <3>;

        qcom,cpu-dma-latency-us = <2 360430>;

        qcom,cpu-dma-latency-us-r = <2 360430>;

        qcom,cpu-dma-latency-us-w = <2 360430>;

        qcom,cpu-affinity-r ="affine_cores";

        qcom,cpu-affinity-mask-r = <0xf0>;

        qcom,modified-dynamic-qos;

 

        qcom,msm-bus,name = "sdhc1";

        qcom,msm-bus,num-cases = <9>;

        qcom,msm-bus,num-paths = <1>;

        qcom,msm-bus,vectors-KBps = <78 512 00>, /* No vote */

            <78 512 1046 3200>,    /* 400 KB/s*/

            <78 512 52286 160000>, /* 20MB/s */

            <78 512 65360 200000>, /* 25MB/s */

            <78 512 130718 400000>, /* 50MB/s */

            <78 512 130718 400000>, /* 100MB/s */

            <78 512 261438 800000>, /* 200MB/s */

            <78 512 261438 800000>, /* 400MB/s */

            <78 512 1338562 4096000>; /*Max. bandwidth */

        qcom,bus-bw-vectors-bps = <0 40000020000000 25000000 50000000

            100000000 200000000 400000000 4294967295>;

 

        clocks = <&clock_gccclk_gcc_sdcc1_ahb_clk>,

             <&clock_gcc clk_gcc_sdcc1_apps_clk>,

             <&clock_gccclk_gcc_sdcc1_ice_core_clk>;

        clock-names = "iface_clk","core_clk", "ice_core_clk";

 

        qcom,clk-rates = <400000 2500000050000000 100000000 192000000 384000000>;

        qcom,ice-clk-rates = <200000000100000000>;

        qcom,bus-speed-mode ="HS400_1p8v", "HS200_1p8v", "DDR_1p8v";

        qcom,scaling-lower-bus-speed-mode ="DDR52";

        status = "disabled";

    };

 

 

    sdhc_2: sdhci@7864000 {

        compatible = "qcom,sdhci-msm";

        reg = <0x7864900 0x11c>,<0x7864000 0x800>;

        reg-names = "hc_mem","core_mem";

 

        interrupts = <0 125 0>, <0 2210>;

        interrupt-names = "hc_irq","pwr_irq";

 

        qcom,bus-width = <4>;

 

        qcom,cpu-dma-latency-us = <701>;

        qcom,msm-bus,name = "sdhc2";

        qcom,msm-bus,num-cases = <8>;

        qcom,msm-bus,num-paths = <1>;

        qcom,msm-bus,vectors-KBps = <81 512 00>, /* No vote */

            <81 512 1046 3200>,    /* 400 KB/s*/

            <81 512 52286 160000>, /* 20MB/s */

            <81 512 65360 200000>, /* 25MB/s */

            <81 512 130718 400000>, /* 50MB/s */

            <81 512 261438 800000>, /* 100MB/s */

            <81 512 261438 800000>, /* 200MB/s */

            <81 512 1338562 4096000>; /*Max. bandwidth */

        qcom,bus-bw-vectors-bps = <0 40000020000000 25000000 50000000

                        100000000 2000000004294967295>;

 

        clocks = <&clock_gccclk_gcc_sdcc2_ahb_clk>,

             <&clock_gcc clk_gcc_sdcc2_apps_clk>;

        clock-names = "iface_clk","core_clk";

 

        qcom,clk-rates = <400000 2500000050000000 100000000 200000000>;

 

        status = "disabled";

    };

 

 

 

 

在MSM8952-mtp.dtsi定义:

 

// msm8952-mtp.dtsi

&sdhc_1 {

    vdd-supply = <&pm8950_l8>;

    qcom,vdd-voltage-level = <29000002900000>;

    qcom,vdd-current-level = <200 570000>;

 

    vdd-io-supply = <&pm8950_l5>;

    qcom,vdd-io-always-on;

    qcom,vdd-io-lpm-sup;

    qcom,vdd-io-voltage-level = <18000001800000>;

    qcom,vdd-io-current-level = <200325000>;

 

    pinctrl-names = "active","sleep";

    pinctrl-0 = <&sdc1_clk_on&sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;

    pinctrl-1 = <&sdc1_clk_off&sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;

 

    qcom,nonremovable;

 

    status = "ok";

};

 

&sdhc_2 {

    vdd-supply = <&pm8950_l11>;

    qcom,vdd-voltage-level = <29500002950000>;

    qcom,vdd-current-level = <15000400000>;

 

    vdd-io-supply = <&pm8950_l12>;

    qcom,vdd-io-voltage-level = <18000002950000>;

    qcom,vdd-io-current-level = <20022000>;

 

    pinctrl-names = "active","sleep";

    pinctrl-0 = <&sdc2_clk_on&sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;

    pinctrl-1 = <&sdc2_clk_off&sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;

 

    #address-cells = <0>;

    interrupt-parent = <&sdhc_2>;

    interrupts = <0 1 2>;

    #interrupt-cells = <1>;

    interrupt-map-mask = <0xffffffff>;

    interrupt-map = <0 &intc 0 125 0

            1 &intc 0 221 0

            2 &msm_gpio 67 0>;

    interrupt-names = "hc_irq","pwr_irq", "status_irq";

    cd-gpios = <&msm_gpio 67 0x1>;

 

    status = "ok";

};

 

 

detect检测管脚:

//msm8952-CPA8_931-P0.dtsi.c(dts) 39207  2016/3/31

// SD卡检测管脚配置:msm8952-CPA8_931-P0.dtsi.c

/* Begin add pinmux*/

&tlmm_pinmux {

        

        /delete-node/ cross-conn-det;

        /delete-node/tpiu_setb_1;

        /delete-node/tpiu_setb_2;

        /delete-node/tpiu_setb_3;

        /delete-node/tpiu_setb_4;

 

        /*configure sdcard gpio*/  

        sdhc2_cd_pin {

                        qcom,pins = <&gp67>;

                        qcom,num-grp-pins =<1>;

                        qcom,pin-func = <0>;

                        label ="cd-gpio";

                        sdc2_cd_on: cd_on {

                                drive-strength= <2>;

                                bias-disable;

                        };

                        sdc2_cd_off: cd_off {

                                drive-strength= <2>;

                                bias-disable;

                        };

                };

 

}
msm8952-CPA8_931-P0.dtsi.c (dts)   39207  2016/3/31

4.3 模块时钟


 参考文档80-NV610-4_MSM8952CLOCK PLAN.pdf

如图为系统内部分时钟,GPLL4为SDCC提供时钟,并最大可以达到1.2GHZ;经过锁相环、分频等得到EMMC和SD卡所需要的时钟;

 

在dts里设置的速率有:

        qcom,msm-bus,vectors-KBps = <78 512 00>, /* No vote */

            <78 512 1046 3200>,    /* 400 KB/s*/

            <78 512 52286 160000>, /* 20MB/s */

            <78 512 65360 200000>, /* 25MB/s */

            <78 512 130718 400000>, /* 50MB/s */

            <78 512 130718 400000>, /* 100MB/s */

            <78 512 261438 800000>, /* 200MB/s */

            <78 512 261438 800000>, /* 400MB/s */

            <78 512 1338562 4096000>; /*Max. bandwidth */


   

    当EMMC支持HS200,可以达到200MHZ;HS400模式,可以达到400MHZ

    SD3.0,最大速度50MHZ;

 

 

 4.4 模块中断

SD使用两个中断资源分别为detectpin中断和SD控制器中断。

 

中断

中断名称

中断号

中断方式

控制器中断

hc_irq

125

cpu内部中断

控制器中断

pwr_irq

221

cpu内部中断

Detect 中断

Gpio 67

边沿触发

SDIO控制器中断用于当SD在传输,出错,FIFO满等状态下产生,当此中断产生后读取MCI_STATUS寄存器判断是什么事件导致产生中断,并对其进行相应处理,而Detect 中断则在插拔卡时产生。

 

4.5DMA

 

在arch\arm\mach-msm\include\mach\dma.h中如下定义

 

#define DMOV_SDC1_CHAN        8

#define DMOV_SDC1_CRCI        6

 

#define DMOV_SDC2_CHAN        8

#define DMOV_SDC2_CRCI        7

 

#define DMOV_SDC3_CHAN        8

#define DMOV_SDC3_CRCI        12

 

#define DMOV_SDC4_CHAN        8

#define DMOV_SDC4_CRCI        13

 

 

在代码中可以设置的宏定义如下:

#define SDHCI_USE_SDMA      (1<<0) /* Host is SDMA capable */

#define SDHCI_USE_ADMA      (1<<1) /* Host is ADMA capable */

#define SDHCI_REQ_USE_DMA   (1<<2) /*Use DMA for this req. */

 

 

 

工作状态机切换示意图如下,参考:80-NL199-1_SDHCI ARCHITECTURE AND DEBUGGING.pdf

 Android平台_驱动_SD_软件 概要设计说明书_第15张图片

 

5 软件设计

5.1    块设备驱动;

5.1.1  块设备基本概念:

Linux设备驱动分三类,字符驱动、块设备驱动、网络设备驱动;存储类使用块设备,进行高效的数据传输管理;

块设备:是一种能够具有一定结构的随机存取设备,读写按块进行;使用缓冲区存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次那个姓读取到缓冲区;

与之不同的字符设备:是一种顺序的数据流设备,按字符进行;连续的字符形成一个数据流,不具备缓冲区,读写是实时的;

 

块设备和字符设备最大的区别在于读写数据的基本单元不同。

块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节。

从实现角度来看,字符设备的实现比较简单,内核例程和用户态API一一对应,这种映射关系由字符设备的file_operations维护。块设备接口则相对复杂,读写API没有直接到块设备层,而是直接到文件系统层,然后再由文件系统层发起读写请求。

 

Linux内核与块设备,处理数据的基本单元有所区别;

扇区(sectors):任何块设备对数据处理的基本单位;通常,1sector = 512Byte

块(block:有Linux指定的,对内核或文件系统数据处理的基本单位;1block有N个sector组成;

段(segment):若干个相邻的block组成,是Linux内存管理机制中内存的组成部分;

 

关系如下:

Android平台_驱动_SD_软件 概要设计说明书_第16张图片

总结:(linux数据在内存中处理,以页为单位进行,到文件系统以块为单位进行,传输到块设备则以扇区为单位处理)

Android平台_驱动_SD_软件 概要设计说明书_第17张图片


第一层:文件系统部分:在kernel/fs目录。

文件系统部分三大块:一是上层的文件系统的系统调用,是用户应用层许对内核空间的调用交互方式之一;二是虚拟文件系统 VFS(Virtual Filesystem Switch),三是挂载到 VFS 中的各实际文件系统;

1)系统调用(SystemCall)是操作系统为在用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口;

2)虚拟文件系统(VFS)是linux内核和具体I/O设备之间的封装的一层共通访问接口,通过这层接口,linux内核可以以同一的方式访问各种I/O设备。(open,read, write,ioctl,close的公用接口);虚拟文件系统本身是linux内核的一部分,是纯软件的东西,并不需要任何硬件的支持。

3)实际文件系统,对实际的设备数据进行布局和寻址;对于EMMC,常用ext2,ext3,ext4等;对SD卡,常用FAT,FAT32,exFAT等文件系统;包含磁盘文件系统,和固态硬盘文件系统;前者用于电脑的硬盘,后者则是手机的存储器;对于原有的nand flash则是mtd文件系统,如jffs2,yaffs2等;

Android平台_驱动_SD_软件 概要设计说明书_第18张图片


 

 

第二层:块设备中间层:在kernel/block/kernel/drivers/block/目录

1)  通用块层(GenericBlock Layer):在Linux中,驱动对块设备的输入或输出(I/O)操作,都会向块设备发出一个请求,在驱动中用request结构体描述。由通用块层(Generic Block Layer)负责维持一个I/O请求在上层文件系统与底层物理磁盘之间的关系。在通用块层中,通常用一个bio结构体来对应一个I/O请求。

Linux提供了一个gendisk数据结构体,用来表示一个独立的磁盘设备或分区,用于对底层物理磁盘进行访问。在gendisk中有一个类似字符设备中file_operations的硬件操作结构指针,是block_device_operations结构体。

2)  I/O调度程序层(IOScheduler Layer):但对于一些磁盘设备而言请求的速度很慢,这时候内核就提供一种队列的机制把这些I/O请求添加到队列中(即:请求队列),在驱动中用request_queue结构体描述。在向块设备提交这些请求前内核会先执行请求的合并和排序预操作,以提高访问的效率,然后再由内核中的I/O调度程序子系统来负责提交I/O 请求,  调度程序将磁盘资源分配给系统中所有挂起的块 I/O  请求,其工作是管理块设备的请求队列,决定队列中的请求的排列顺序以及什么时候派发请求到设备。

 

当多个请求提交给块设备时,执行效率依赖于请求的顺序。如果所有的请求是同一个方向(如:写数据),执行效率是最大的。内核在调用块设备驱动程序例程处理请求之前,先收集I/O请求并将请求排序,然后,将连续扇区操作的多个请求进行合并以提高执行效率(内核算法会自己做,不用你管),对I/O请求排序的算法称为电梯算法(elevatoralgorithm)。电梯算法在I/O调度层完成。内核提供了不同类型的电梯算法,电梯算法有

1 noop(实现简单的FIFO,基本的直接合并与排序),

2anticipatory(延迟I/O请求,进行临界区的优化排序),

3Deadline(针对anticipatory缺点进行改善,降低延迟时间),

4 Cfq(均匀分配I/O带宽,公平机制)

PS:其实IO调度层(包括请求合并排序算法)是不需要用户管的,内核已经做好

相关数据结构

block_device:      描述一个分区或整个磁盘对内核的一个块设备实例

block_device_operations: 描述块设备的具体操作;

gendisk:               描述一个通用硬盘(generic hard disk)对象。

hd_struct:             描述分区应有的分区信息

bio:                        描述块数据传送时怎样完成填充或读取块给driver

request:                描述向内核请求一个列表准备做队列处理。

request_queue:  描述内核申请request资源建立请求链表并填写BIO形成队列。

 

第三层:Linux mmc子系统(kernel/drivers/mmc目录)


Linuxkernel把mmc,sd以及sdio三者的驱动代码整合在一起,俗称mmc子系统。源码位于drivers/mmc下。其下有三个子目录,分别是:

 card 

core 

host

其中,

1) card用于构建一个块设备作为上层与mmc子系统沟通的桥梁;把操作的数据以块设备的处理方式写到记忆体上或从记忆体上读取

2) core抽象了mmc,sd,sdio三者的通用操作;将数据以何种格式,何种方式在MMC/SD主机控制器与MMC/SD卡的记忆体(即块设备)之间进行传递,这种格式、方式被称之为规范或协议

3) host则是高通平台上的host驱动代码,动手实现的具体MMC/SD设备驱动了。


第四层:实际的文件传输;

 

由host到device的传输;有command和data线传输实际的数据;

 

参考EMMC协议的传输状态机;

根据不同的操作方式,进入不同的状态,可以通过响应的CMD命令进行出发;

1,识别过程:

2,传输模式:

5.2 驱动初始化

 驱动的初始化顺序:(参考system.map内核符号表,可以查看到init初始化加载的顺序);

 Android平台_驱动_SD_软件 概要设计说明书_第19张图片

 

mmc主要的结构体:

1. struct mmc_host 用来描述卡控制器位kernel/include/linux/mmc/host.h下面。

a)  使用sdhci标准接口,所以有了多级host的嵌套;

b)  mmc_host->private = (struct sdhci_host*)host;

c)  host->private = (struct sdhci_pltfm_host*)pltfm_host;

d)  pltfm_host->private = (struct sdhci_msm_host*)msm_host;

2. struct cmdq_host_ops,基于新特性command queue,添加的操作函数;

3. struct mmc_card 用来描述卡位于kernel/include/linux/mmc/card.h下面

4. struct mmc_driver 用来描述mmc卡驱动在kernel/include/linux/mmc/card.h下面。

5. struct file_operations,文件系统针对用户程序提供的接口;也是对底层最大程度的封装。

6. struct mmc_host_ops用来描述卡控制器操作集,用于从主机控制器向core层注册操作函数,从而将core层与具体的主机控制器隔离。也就是说core要操作主机控制器,就是这个ops当中给的函数指针操作,不能直接调用具体主控制器的函数。 位于kernel/include/linux/mmc/host.h下面。

7. struct mmc_ios用于描述了控制器对卡的I/O状态。位于kernel/include/linux/mmc/host.h下面。

8. struct mmc_request用于描述读写MMC卡的请求,它包括命令,数据以及请求完成后的回调函数。位于kernel/include/linux/mmc/core.h中。

9. struct mmc_queue是MMC的请求队列结构,它封装了通用请求队列结构,加入了MMC卡相关结构。位于kernel/drivers/mmc/card/queue.h中。

10.structmmc_data描述了MMC卡读写的数据相关信息,如:请求,操作命令,数据以及状态等。位于kernel/include/linux/mmc/core.h中。

11.structmmc_blk_data描述块设备的数据;

12.structmmc_command描述了MMC卡操作相关命令及数据,状态信息等。位于kernel/include/linux/mmc/core.h中。

13.structmmc_cid, mmc_csd, mmc_ext_csd, sd_scr, sd_ssr等,读写配置存储器的寄存器;

 

重要函数:

 

    host主要函数:

structmmc_host *mmc_alloc_host(int extra, struct device *dev)

intmmc_add_host(struct mmc_host *host)

voidmmc_remove_host(struct mmc_host *host)

voidmmc_remove_host(struct mmc_host *host)

 

card主要函数:

staticstruct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)

staticint mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)

staticint mmc_add_disk(struct mmc_blk_data *md)

 

mmc_driver主要函数:

extern int mmc_register_driver(struct mmc_driver *);

extern void mmc_unregister_driver(struct mmc_driver *);

 

 

mmc_blk主要函数:

int register_blkdev(unsigned int major, const char *name)

staticstruct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,

                        struct device *parent,

                        sector_t size,

                        bool default_ro,

                        const char *subname,

                        int area_type)

staticint mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)

staticvoid mmc_blk_remove_req(struct mmc_blk_data *md)

 

 

 

通信的基础

参考协议中的command, host等时序进行通信;

 

按照EMMC和SD3.0的协议,内置CID,CSD,EXT_CSD等寄存器,保存了各项信息;保存了MMC的工作条件和支持项,host根据寄存器的值做适合通信的配置初始化;

structmmc_cid ;

structmmc_csd ;

structmmc_ext_csd ;

对mmc的操作具体函数实现:

mmc_init_card()读取具体的寄存器值,并做出初始化配置;

并通过device_create_file(),将其在/sys目录下反馈;

 

在mmc_ops.h里有各个具体的操作函数;

intmmc_select_card(struct mmc_card *card);

intmmc_deselect_cards(struct mmc_host *host);

intmmc_go_idle(struct mmc_host *host);

intmmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);

intmmc_all_send_cid(struct mmc_host *host, u32 *cid);

intmmc_set_relative_addr(struct mmc_card *card);

intmmc_send_csd(struct mmc_card *card, u32 *csd);

intmmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);

intmmc_send_status(struct mmc_card *card, u32 *status);

intmmc_send_cid(struct mmc_host *host, u32 *cid);

intmmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);

intmmc_spi_set_crc(struct mmc_host *host, int use_crc);

intmmc_card_sleepawake(struct mmc_host *host, int sleep);

intmmc_bus_test(struct mmc_card *card, u8 bus_width);

intmmc_send_hpi_cmd(struct mmc_card *card, u32 *status);

int mmc_discard_queue(structmmc_host *host, u32 tasks);

 

通过device_create_file(),将其反馈到/sys目录下显示,并进行交互;方便调试;

 

 

block主要结构体:
    结构体block_device代表一个块设备对象,如:整个硬盘或特定分区。如果该结构代表一个分区,则其成员bd_part指向设备的分区结构。如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk

结构体gendisk代表了一个通用硬盘(generic hard disk)对象,它存储了一个硬盘的信息,包括请求队列、分区链表和块设备操作函数集等。块设备驱动程序分配结构gendisk实例,装载分区表,分配请求队列并填充结构的其他域。

结构体block_device_operations字符设备通过 file_operations 操作结构使它们的操作对系统可用. 一个类似的结构用在块设备上是 struct block_device_operations,定义在 .

block_device_operations是块设备对应的操作接口,是连接抽象的块设备操作与具体块设备操作之间的枢纽。并不能完全提供文件操作全部的API,实际上只提供了open、release等函数,其他的文件操作依赖于def_blk_fops:

conststruct file_operations def_blk_fops = {

    .open      =blkdev_open,

    .release   =blkdev_close,

    .llseek       =block_llseek,

    .read      =do_sync_read,

    .write     =do_sync_write,

    .aio_read  =blkdev_aio_read,

    .aio_write =blkdev_aio_write,

    .mmap      =generic_file_mmap,

    .fsync     =blkdev_fsync,

    .unlocked_ioctl   = block_ioctl,

#ifdefCONFIG_COMPAT

    .compat_ioctl = compat_blkdev_ioctl,

#endif

    .splice_read  =generic_file_splice_read,

    .splice_write = generic_file_splice_write,

};

};

结构request代表了挂起的I/O请求,每个请求用一个结构request实例描述,存放在请求队列链表中,由电梯算法进行排序,每个请求包含1个或多个结构bio实例;IO调度算法可将连续的bio合并成1个请求。所以,1个请求可以包含多个bio。

请求队列结构request_queue,每个块设备都有一个请求队列,每个请求队列单独执行I/O调度,请求队列是由请求结构实例链接成的双向链表,链表以及整个队列的信息用结构request_queue描述,称为请求队列对象结构或请求队列结构。它存放了关于挂起请求的信息以及管理请求队列(如:电梯算法)所需要的信息。结构成员request_fn是来自设备驱动程序的请求处理函数。

 Android平台_驱动_SD_软件 概要设计说明书_第20张图片

 

 

重要函数:

块设备的注册与注销:

intregister_blkdev(unsigned int major, const char *name);

int unregister_blkdev(unsigned int major, const char*name);

 

对磁盘:

externvoid add_disk(struct gendisk *disk);

extern voiddel_gendisk(struct gendisk *gp);

 

队列:

structrequest_queue *blk_alloc_queue(gfp_t gfp_mask)

structrequest_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)

structrequest *blk_make_request(struct request_queue *q, struct bio *bio, gfp_t gfp_mask)


 


6 描述重要和常用的数据结构等

 

 

 

 

static structsdhci_ops sdhci_msm_ops = {

    .crypto_engine_cfg = sdhci_msm_ice_cfg,

    .crypto_cfg_reset = sdhci_msm_ice_cfg_reset,

    .crypto_engine_reset = sdhci_msm_ice_reset,

    .platform_reset_enter =sdhci_msm_reset_enter,

    .set_uhs_signaling =sdhci_msm_set_uhs_signaling,

    .check_power_status =sdhci_msm_check_power_status,

    .execute_tuning = sdhci_msm_execute_tuning,

    .enhanced_strobe =sdhci_msm_enhanced_strobe,

    .toggle_cdr = sdhci_msm_toggle_cdr,

    .get_max_segments = sdhci_msm_max_segs,

    .set_clock = sdhci_msm_set_clock,

    .get_min_clock = sdhci_msm_get_min_clock,

    .get_max_clock = sdhci_msm_get_max_clock,

    .disable_data_xfer =sdhci_msm_disable_data_xfer,

    .dump_vendor_regs =sdhci_msm_dump_vendor_regs,

    .config_auto_tuning_cmd =sdhci_msm_config_auto_tuning_cmd,

    .enable_controller_clock =sdhci_msm_enable_controller_clock,

    .reset_workaround = sdhci_msm_reset_workaround,

    .clear_set_dumpregs =sdhci_msm_clear_set_dumpregs,

    .notify_load = sdhci_msm_notify_load,

    .notify_pm_status =sdhci_msm_notify_pm_status,

    .enhanced_strobe_mask =sdhci_msm_enhanced_strobe_mask,

};

 

其中调用了sdhci的公共接口:

 

static const structmmc_host_ops sdhci_ops = {

    .pre_req   =sdhci_pre_req,

    .post_req  =sdhci_post_req,

    .request   =sdhci_request,

    .set_ios   =sdhci_set_ios,

    .get_cd       =sdhci_get_cd,

    .get_ro       =sdhci_get_ro,

    .hw_reset  =sdhci_hw_reset,

    .enable_sdio_irq = sdhci_enable_sdio_irq,

    .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,

    .execute_tuning          = sdhci_execute_tuning,

    .enhanced_strobe     = sdhci_enhanced_strobe,

    .card_event          =sdhci_card_event,

    .card_busy =sdhci_card_busy,

    .enable       =sdhci_enable,

    .disable   =sdhci_disable,

    .stop_request = sdhci_stop_request,

    .get_xfer_remain = sdhci_get_xfer_remain,

    .notify_load  =sdhci_notify_load,

    .notify_pm_status = sdhci_notify_pm_status,

};

 

 

 

 

    驱动模型,按照总线,设备,驱动的形式来大概讲解框架;

总线:由mmc总线和sdio总线:

static structbus_type mmc_bus_type = {

    .name      ="mmc",

    .dev_attrs =mmc_dev_attrs,

    .match     =mmc_bus_match,

    .uevent       =mmc_bus_uevent,

    .probe     =mmc_bus_probe,

    .remove       =mmc_bus_remove,

    .shutdown        = mmc_bus_shutdown,

    .pm    =&mmc_bus_pm_ops,

};

static structbus_type sdio_bus_type = {

    .name      ="sdio",

    .dev_attrs =sdio_dev_attrs,

    .match     =sdio_bus_match,

    .uevent       =sdio_bus_uevent,

    .probe     =sdio_bus_probe,

    .remove       =sdio_bus_remove,

    .pm    =SDIO_PM_OPS_PTR,

};

分别通过mmc_register_bus()和sdio_register_bus()进行注册;

 

总线操作:

static const structmmc_bus_ops mmc_ops = {

    .awake = mmc_awake,

    .sleep = mmc_sleep,

    .remove = mmc_remove,

    .detect = mmc_detect,

    .suspend = NULL,

    .resume = NULL,

    .power_restore = mmc_power_restore,

    .alive = mmc_alive,

    .change_bus_speed = mmc_change_bus_speed,

};

static const structmmc_bus_ops mmc_sd_ops = {

    .remove = mmc_sd_remove,

    .detect = mmc_sd_detect,

    .suspend = NULL,

    .resume = NULL,

    .power_restore = mmc_sd_power_restore,

    .alive = mmc_sd_alive,

    .change_bus_speed = mmc_sd_change_bus_speed,

};

static const structmmc_bus_ops mmc_sdio_ops = {

    .remove = mmc_sdio_remove,

    .detect = mmc_sdio_detect,

    .suspend = mmc_sdio_suspend,

    .resume = mmc_sdio_resume,

    .power_restore = mmc_sdio_power_restore,

    .alive = mmc_sdio_alive,

};

最上层的操作:

static const structblock_device_operations mmc_bdops = {

    .open         =mmc_blk_open,

    .release      =mmc_blk_release,

    .getgeo           =mmc_blk_getgeo,

    .owner        =THIS_MODULE,

    .ioctl        =mmc_blk_ioctl,

#ifdef CONFIG_COMPAT

    .compat_ioctl     = mmc_blk_compat_ioctl,

#endif

};

 

 

 

const struct file_operationsext4_dir_operations = {

    .llseek     =ext4_dir_llseek,

    .read       =generic_read_dir,

    .readdir    =ext4_readdir,

    .unlocked_ioctl = ext4_ioctl,

#ifdef CONFIG_COMPAT

    .compat_ioctl   = ext4_compat_ioctl,

#endif

    .fsync      =ext4_sync_file,

    .release    =ext4_release_dir,

};

const struct file_operationsfat_file_operations = {

    .llseek     =generic_file_llseek,

    .read       =do_sync_read,

    .write      =do_sync_write,

    .aio_read   =generic_file_aio_read,

    .aio_write  =generic_file_aio_write,

    .mmap       =generic_file_mmap,

    .release    =fat_file_release,

    .unlocked_ioctl = fat_generic_ioctl,

#ifdef CONFIG_COMPAT

    .compat_ioctl   = fat_generic_compat_ioctl,

#endif

    .fsync      =fat_file_fsync,

    .splice_read    =generic_file_splice_read,

};

 

 

文件信息的打印:

void ext4_msg(structsuper_block *sb, const char *prefix, const char *fmt, ...)

{

    struct va_format vaf;

    va_list args;

 

    va_start(args, fmt);

    vaf.fmt = fmt;

    vaf.va = &args;

    printk_ratelimited("%sEXT4-fs (%s):%pV\n", prefix, sb->s_id, &vaf);

    va_end(args);

}

如:

ext4_msg(sb,KERN_INFO, "recovery complete");

[010108:00:28.335140]@4 EXT4-fs (mmcblk0p27): recovery complete

 

fat_msg(sb,KERN_WARNING, "Volume was not properly "

    "unmounted. Some data may be corrupt."

    "Please run fsck.");

[0101 08:00:52.213219]@7FAT-fs (mmcblk1p1): Volume was not properly unmounted. Some data may becorrupt. Please run fsck.


 

 

 

 

 


7接口文件

1.  SD host层对SD core 层的接口

内部接口函数名

功能

备注

msmsdcc_enable

在该函数中打开SD 的时钟

 

msmsdcc_disable

该函数主要关闭SD的时钟

 

msmsdcc_request

处理SD的所有访问请求,对SD的所有操作都要经过该函数

 

msmsdcc_set_ios   sdcc_set_ios

该函数是对SD控制器的操作函数,主要用于配置SD的控制器

 

msmsdcc_get_ro

确认SD卡是否为写保护

 

msmsdcc_enable_sdio_irq

对SDIO 设备的操作函数,SD驱动未使用

 

msmsdcc_start_signal_voltage_switch

SD的IO电压进行设置切

换到该模式

 

msmsdcc_execute_tuning

UHS 型SD卡用于调整时序

 

 

 

 

 

 

 

 

 

 

 

 

 

2.  块设备向文件系统层接口

内部接口函数名

功能

备注

mmc_blk_open

当文件描述符被打开时调用

 

mmc_blk_release

当文件被关闭时调用

 

mmc_blk_getgeo

获得SD的柱面,头,块信息

 

mmc_blk_ioctl 

当用Ioctl函数访问SD设备文件时调用该函数

 

mmc_blk_compat_ioctl,

与mmc_blk_ioctl 函数作用相同,在该驱动中未使用

 

 


1.  对磁盘的访问节点为

/dev/block/mmcblk1*当该SD卡还有分区时*可以从p1开始到p25,一般对SD的访问都是通过挂载的方式访问,在通过各文件节点对SD上的数据访问。也可以用字符设备的方式访问,这样只能按照IO命令的方式进行操作。

2. SD卡一些特征在filesystem中的节点

/sys/devices/platform/msm_sdcc.3/mmc_host/mmc1/mmc1:1234/block/mmcblk0

 

7.3      接口文件

内核提供给用户空间的接口有:

设备文件接口

功能描述

/dev/ mmcblk1

SD卡接口

/sys/devices/platform/msm_sdcc.2/mmc_host/mmc1

SD属性文件接口

 

 

 

8      模块组件视图


你可能感兴趣的:(android,驱动开发)