|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
Linux设备驱动子系统9 U) G" s& J2 n& e" ^) O( }& v9 o
* @ @0 `. ~0 X$ }& A; R
1. 总论9 i( ~0 j6 {! u* o; F( x
2. 主控制器: x! {% h. r6 Q$ R8 E) Q) n$ H3 c
3. 协议层
" i* |/ w5 s2 s! c, Q6 _* q g4. 块设备
0 @+ y: ]' T( M9 ^! i' l% }; j2 |4 t* ^) K" s6 A W! D' D& S
: _* e8 b& E( x) i1. 总论
2 o+ S$ `) O3 w8 }5 z1 i' m; A: x' R5 J6 l
1.1 概念
* ]4 I P5 B C$ I7 e6 \3 F. E* p5 L' @. M4 M( i( \
- MMC - MultiMedia Card
- SD - Secure Digital Card
( I1 n% @4 E' ~4 @0 D( N' s
( T+ F* f% T3 @* W1.2 分类5 f! [% ], c/ ?
6 y# q+ m! M% M
- 按存储大小,普通SD卡(<=2GB,支持FAT12/FAT16),HCSD卡(>2GB,<=32GB,支持FAT32)
- 按体积大小,普通SD卡,mini-SD卡,micro-SD卡(TF卡)+ _7 k) ^6 {9 e# h1 D
/ i6 k/ b1 P% x* l3 _/ D: K% @
2 O* E, V4 u/ b. o* w1.3 速度
4 n1 t8 l. u# \0 |+ m. d2 \' Y" k* I+ x/ J0 t
- 默认模式: 12.5MB/s
- 高速模式: 25MB/s
4 F u8 Q- ]) r0 z& M
" Y* ]+ z) B' h5 D( N6 i5 I* R2 @- ~3 I% p9 X! e
1.4 子系统代码结构
A* T* K$ l) I; q `
$ j8 n/ i4 S) m3 K Linux源码里/drivers/mmc下有三个文件夹,分别存放了SD块设备,核心层和SD主控制器的相关代码,可以通过Kconfig和Makefile获取更多信息。0 M& K& m) O3 `; s1 U
, V2 A p4 s( D. K
3 [ Z# c& @. h# k0 n* @3 ?2. 主控制器
# S; q* P. J& B0 y$ ~8 `; ~/ n0 S7 e; `1 W( Q
SD卡的控制器芯片,可以看成CPU的代言人,它为CPU分担了完成与SD卡数据通信的任务。6 J$ c8 s8 g& {( e1 d
. W+ M5 }+ T5 L6 R3 a2.1 数据结构$ R0 N1 s }/ ^. B* U t
2 _! h5 L0 ] m
以PXA芯片的SD控制器驱动为例,
4 p7 |, ~9 P F8 B8 w
7 Z( n0 m: V5 ~+ qstruct pxamci_host {. y9 U3 d5 e4 }" H; ?8 H: V# L+ e
struct mmc_host *mmc;5 e. K1 \ @! B, K* V8 X
2 [ ?9 ?4 L3 {( v" j& h struct mmc_request *mrq;
, `, `9 C5 e' v- X" o6 M E0 A struct mmc_command *cmd;
1 T1 ] z5 X4 e" `2 c5 ^1 }7 t% } struct mmc_data *data;) V: g" G2 j2 b) m
1 L2 e8 O: K7 _) S6 z" A4 l ... ...
2 F& b+ z H) a1 ]};1 V* k) I1 k# ^( Q! i. Y
9 v3 o( b: D2 W
2.1.1 struct mmc_host
9 y' X% D* V" E9 V; A: u2 F0 _' q. O7 d; h! L' S% [
结构体mmc_host定义于/include/linux/mmc/host.c,可以认为是linux为SD卡控制器专门准备的一个类,该类里面的成 员是所有SD卡控制器都需要的,放之四海而皆准的数据结构,而在PXA芯片控制器的驱动程序pxamci.c中,则为该类具体化了一个对象struct mmc_host *mmc,此mmc指针即指代着该PXA芯片SD卡控制器的一个具体化对象。
, G' g8 _3 e, k" O% G8 E( @$ S1 p; F7 O: n I7 j1 K1 H9 ]5 W
struct mmc_host {9 T: u% {" U3 z
const struct mmc_host_ops *ops; // SD卡主控制器的操作函数,即该控制器所具备的驱动能力
% h: q f8 K- x1 y8 m7 ]- V$ B/ Y; b5 l4 E
const struct mmc_bus_ops *bus_ops; // SD总线驱动的操作函数,即SD总线所具备的驱动能力
4 Z% |( Y& S- ]/ H% z0 U9 b
$ y) D- q1 b8 W2 t1 N. ] struct mmc_ios ios; // 配置时钟、总线、电源、片选、时序等
* i3 W6 e! X) a, k
4 f7 \* g" d( | ^7 \ struct mmc_card *card; // 连接到此主控制器的SD卡设备$ s N% S! l( E6 \, h; z; B
... ...
0 r# b0 f4 k% Z9 n; C};7 r2 {1 e( _9 z( y+ n
+ X& A5 Q. }$ ~7 l/ K
; X& a! C6 p8 H1 g+ C
! N' a4 p) ?3 X- v4 f7 Q" X8 vstruct mmc_host_ops {: _, f% G p9 F' r
void (*request)(struct mmc_host *host, struct mmc_request *req); // 核心函数,完成主控制器与SD卡设备之间的数据通信
( M5 M& L1 r0 A! D. ~" O ^ void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); // 配置时钟、总线、电源、片选、时序等
* Y" Z5 ?/ L4 O& E) M& g% d( S& E int (*get_ro)(struct mmc_host *host);+ l. j. W" L' h
void (*enable_sdio_irq)(struct mmc_host *host, int enable);) A) Y6 I1 P: P8 Y( V
}; }- t/ s" b0 J' g% ]- l
" t9 H) {" z7 P% F- }) D
struct mmc_bus_ops {
" H% m) y T* P5 ]5 `: ? void (*remove)(struct mmc_host *); // 拔出SD卡的回调函数
# m! ?; J: X* y6 i9 r Z% A void (*detect)(struct mmc_host *); // 探测SD卡是否还在SD总线上的回调函数' Z! D" f0 o1 I, A
void (*suspend)(struct mmc_host *);; o7 {" Z& k: X; C1 ~# G5 C) v3 v
void (*resume)(struct mmc_host *);
* c# O3 H8 v q& ^4 s5 X( v% y};
' Q6 O4 h1 L. }9 a' w& Q
1 Q6 q9 N# Q/ S3 d( ~, Qstruct mmc_card {
: \* M6 H' t7 w$ y struct mmc_host *host; /* the host this device belongs to */
8 V9 b l1 F8 u7 ?6 u+ T2 X- J, F: N, Q struct device dev; /* the device */
5 t5 b9 j* q5 W2 g( L/ g* Y unsigned int rca; /* relative card address of device */
. ^* n5 |; n0 Q unsigned int type; /* card type */4 {$ f) Z _5 r" q# C, l
unsigned int state; /* (our) card state */# |7 f b; w/ L$ I: i; J
unsigned int quirks; /* card quirks */
/ C+ p2 E8 w# K N
" c4 o% R1 s/ {) n- T2 L3 k6 w u32 raw_cid[4]; /* raw card CID */' e. [9 B2 y. ^7 M
u32 raw_csd[4]; /* raw card CSD */
$ @, V% {* A. ], Z3 o6 F u32 raw_scr[2]; /* raw card SCR */
# b7 x( d* i7 S5 c struct mmc_cid cid; /* card identification */
7 p5 j# N$ ]/ v' x2 H struct mmc_csd csd; /* card specific */" p: n* H* b# U1 q9 i6 x) h
struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */
6 `5 _1 @: r6 P- ~6 f4 A struct sd_scr scr; /* extra SD information */
+ d3 `* k- a0 |2 [' V$ a0 { struct sd_switch_caps sw_caps; /* switch (CMD6) caps */
9 ]+ e; N. W- V! J8 j, T# q( D1 I7 P; o
unsigned int sdio_funcs; /* number of SDIO functions */
' Z, ^9 c3 b( ^. m ` C& f struct sdio_cccr cccr; /* common card info */& W% h. E$ L% [( r( `7 j
struct sdio_cis cis; /* common tuple info */) }$ ]8 K5 j: S0 I
... ...4 ]5 P9 h3 o. m. P, b& j( [1 Y0 z
};- B O* ]+ H i, ?) i
mmc_card结构体内的数据结构主要存放SD卡的信息,其中RCA, CID, CSD, SCR为SD卡内部的32位寄存器。5 X; o/ o; }3 B. H& j4 Y, ]$ v: L
( w) c( u0 y" _: g9 x9 _
" _4 O2 H6 e9 _# e! x# b4 I
2.1.2 struct mmc_request) x i1 b$ |7 @* w) c, o* B6 w
; k2 V; T. W$ S; O* Y+ n. j
结构体mmc_request定义于/include/linux/mmc/core.h,它主要存放两大数据结构的指针,分别是cmd和data,顾 名思意,一个为指令,一个为数据,也就是说,mmc_request结构体存放了进行主控制器与sd卡间通信所需要的指令和数据,struct mmc_request, struct mmc_command *cmd, struct mmc_data *data三者之间的关系如下所示, 0 S0 Y4 B: H$ a9 t. S R" ]
6 ?; n. X9 A! V! \) @5 C2 Tstruct mmc_request {
& e4 L4 @8 `, S1 k) L) ] struct mmc_command *cmd;$ q/ O P9 U4 Z1 F6 l
struct mmc_data *data;
5 Q6 A1 f4 A; U9 m8 U) n# x* p: R struct mmc_command *stop;
# l# s4 T0 A! L6 m3 C7 V; F1 U) \3 D9 G, ~7 X+ \
void *done_data; /* completion data */4 o7 X* m x" a' i/ T" ~0 x
void (*done)(struct mmc_request *);/* completion function */5 K1 L1 g" n2 p& G3 L/ C
};0 W1 h+ U6 X3 R+ ~9 |6 g
: G2 b: k+ D O% O/ G
说到结构体mmc_command和mmc_data,就必须说说SD卡的协议了。
p# S1 r4 Q- @- S: O' j4 N4 U- u) c* ^+ z
1) 物理结构
' U3 X% }0 X+ ?
( ~1 \6 f7 ^5 F SD卡有9个pin脚(micro-SD为8个,少一个接地pin脚),如图所示,
; G+ g4 [: R% M+ {% E& F, I$ l- }! z5 X$ {* K( c8 h
2 t! T! W/ {% j+ d& o) J- |
; z, T! Q0 V+ [5 u1 x7 A. o SD的数据传输方式有两种,普通SD模式和SPI模式,以SD模式为例,9个pin脚分别是VDD,VSS,CLK,以及我们需要关注的一根指令线CMD,4根数据线DAT0~DAT3。! D% j$ n% I3 I$ Z
. ?+ { O2 p3 x- \4 k. T! x' ?' @* h 2) 传输模式# \' g4 F/ M9 n; o
4 l7 J0 i5 L8 t
首先由主机向SD卡发送命令command,等待SD卡的回复response,如果成功收到回复,则进行数据传输。其中,指令线和数据线上传输的指令和数据都要遵循相应的协议格式。
& J9 Q% Z+ X/ H6 I/ M+ u# @- N$ }8 r% O) Z/ v* s- Z
V2 s- s! m7 [
0 p6 g" @7 @6 D2 \' [' [
3) 指令格式% U D3 C) e3 c' \
' i1 b: i% V/ I; j j7 e
! Q9 ~2 s% y! \! I
& m, f c( H. S* s6 h( w 一条指令command共48位,其中command index指代这条具体的指令名称,argument为该指令的参数。6 | [ e. U6 \8 \1 M7 B
: k4 l; t" q/ T0 B
一条回复response根据不同的指令有几种不同类型。
4 p& l: G8 Z, I# G
8 u* t' t) B! rstruct mmc_command {4 Z' X0 |8 A0 l4 K
u32 opcode; // 对应command index* B! ]0 d. V1 x3 J! |
u32 arg; // 对应argument
C2 E1 e" a6 ^3 d( e u32 resp[4]; // 对应response
) M$ b& P; u( [7 e8 t5 P unsigned int flags; /* expected response type */
" |6 t' h% l% h7 _# ]7 N
& R- T$ N. B/ _( c ... .... m1 F* e" k9 s* O0 m/ N4 g
/ d& T7 U; j) H! y unsigned int retries; /* max number of retries */: h4 p) h, r [$ S. I Q
unsigned int error; /* command error */" I0 t$ q' a6 C" i1 |/ F
, V! n" H! ? g( l- C struct mmc_data *data; /* data segment associated with cmd */
$ n2 V2 E7 ^& Z struct mmc_request *mrq; /* associated request */7 u9 @: |. X( ^' O0 P! C
};
2 G$ r- c) t. i4 O) V6 g9 w( ?% G" m v9 f1 n6 P: e
4) 数据格式. S3 {( N4 k, n. i# l+ u
2 a& \) a# x# j/ m! s8 q 数据传输按数据线可分为一线传输和四线传输,按数据大小可分为字节传输和块传输(512字节)。
# f0 B* c6 \) C: z% `& ?! L& b- S) `) h0 Y' v! Q7 g/ r- R3 `
struct mmc_data {
3 N5 d Y6 Z: W6 u unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */
' r$ w1 e2 m/ D+ c8 j1 M unsigned int timeout_clks; /* data timeout (in clocks) */+ w: m- D4 i" N, k: J5 C1 a
unsigned int blksz; /* data block size */
8 Y. Q8 |0 K) w1 M8 z unsigned int blocks; /* number of blocks */) T! J, I2 v7 m% ^ q1 r9 U
unsigned int error; /* data error */
; p: @, m! ~" P; l6 R unsigned int flags;) B' h) l9 h7 x7 _0 Z( [, z5 ?; y
7 \" I2 p$ E4 i. @#define MMC_DATA_WRITE (1 << 8)
7 ~/ Y7 O& ^. Y#define MMC_DATA_READ (1 << 9)
3 g$ e, @' e4 M& m6 e, ^1 K#define MMC_DATA_STREAM (1 << 10)8 {4 q [& u9 K" y9 _- c
. h, T. }! L5 t2 [4 R0 X
unsigned int bytes_xfered;3 H v3 M$ B- G7 P
: G, ]+ Z+ S% r- y5 V struct mmc_command *stop; /* stop command */- L6 c: p& _( e3 L$ Y5 v, v
struct mmc_request *mrq; /* associated request */
7 v, ?. B4 v P# C
: g `/ S4 V) N2 o5 |' B' W# ^& A+ Q unsigned int sg_len; /* size of scatter list */
" W8 O5 L$ n9 l& p% s6 J" W struct scatterlist *sg; /* I/O scatter list */
/ u: i& D; s }: w( V};; Y6 P, J, o4 v
" J* J! A" [/ \. `( a: `
2.2 驱动程序3 N. e. R* M. M: A
9 J' W+ \" e0 l' z 系统初始化时扫描platform总线上是否有名为该SD主控制器名字"pxa2xx-mci"的设备,如果有, 驱动程序将主控制器挂载到platform总线上,并注册该驱动程序。
# M0 O0 s7 h% O5 j {, R" \% \" ^# b( {
static int __init pxamci_init(void)
- w0 \$ F, v3 |% W{
9 _+ s6 ?( Z- h" W
7 c& F( O/ x& Q9 V$ Y+ _) x v return platform_driver_register(&pxamci_driver);8 L! R/ [# K: Z, w- M0 m
}! [3 z; V7 i- L% y
. n# [3 v8 |% j C, r2 Z3 r! x2 o) fstatic struct platform_driver pxamci_driver = {5 E6 v, F' H1 l" d3 E
.probe = pxamci_probe,* e+ \( {- R& i0 X( C4 |
.remove = pxamci_remove,, |. H% i* }! z# R5 f
.suspend = pxamci_suspend,1 O0 I2 ?, C/ F, F0 Q) _* s: H* J4 p
.resume = pxamci_resume,
6 q8 H, a& [+ p+ E. S5 i* d .driver = {
& d, `5 k4 m" I' w& ]9 n# F .name = “pxa2xx-mci”,
% B, C: Q4 N& l/ f% C/ Y/ A .owner = THIS_MODULE,. O3 W' ]! O1 Z) w, W, e- x# v* @
},
' A! d- J7 _# p" W0 }}; $ T8 I3 B- }4 f) x! U
: j+ |, F$ i2 @5 b3 F8 s$ _
其中,remove为probe的反操作,suspend和resume涉及电源管理的内容,本文重点讨论probe。+ A, v E6 K a. p; D8 f
" w( z" i' a' y- p' ~
SD主控制器驱动程序的初始化函数probe(struct platform_device *pdev),概括地讲,主要完成五大任务,; O$ {6 A( h7 o" F1 R; B X
- o1 b4 F% y1 o
- 初始化设备的数据结构,并将数据挂载到pdev->dev.driver_data下
- 实现设备驱动的功能函数,如mmc->ops = &pxamci_ops;
- 申请中断函数request_irq()
- 注册设备,即注册kobject,建立sys文件,发送uevent等
- 其他需求,如在/proc/driver下建立用户交互文件等
& X9 r u% L. @ V6 ~5 B ' L; g3 X" b: e7 t' @# @9 B$ O
/ N. L4 f: J u" v9 _8 s( D
2.2.1 注册设备 U, E! O$ q8 T
2 q; M; K. H* K" G/ t! T
对于设备的注册,所有设备驱动的相关代码都类似。
3 K5 X& e( g6 a/ k) B4 C4 j Q$ Q6 {6 M2 f: }5 A+ F
static int pxamci_probe(struct platform_device *pdev)5 `6 h2 z$ K2 ~- y+ V
( |/ {7 S6 Q1 k7 v; s" O{% E+ p7 x5 l0 |' t) S8 S' Z
B9 f+ @: n! i0 x" s. N. V; b. ~ mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
& P* V- _3 e/ Z A0 c# \' U- o
# b* q% }$ b5 v" c. f# a& J7 i mmc_add_host(mmc);
. l0 ` F, B# n8 H9 g6 [' a' u* m. G T6 j/ ?* v- c
... ...0 @0 G2 Z" J" n n6 T8 _# @
7 M7 x' q z5 |1 F& R1 |3 F}
' |' ?( S0 N3 T: }6 x d( n" l9 q _$ Y x1 D) K% c
这两个函数都由/drivers/mmc/core核心层下的host.c负责具体实现,
9 i s* x7 T/ x8 W$ d, M7 v% \' J2 P; e% A) @
1) mmc_alloc_host% Y2 e- l& t) R
' w! @. C+ O" V6 Q+ G8 `3 C* m: \
为主设备控制器建立数据结构,建立kobject,并初始化等待队列,工作队列,以及一些控制器的配置。其中,INIT_DELAYED_WORK(&host->detect, mmc_rescan);将探测SD卡的函数mmc_rescan与工作队列host->detect关联,mmc_rescan是整个SD子系统 的核心函数,本文第三部分协议层将对它作重点讨论。6 k' C6 M5 e3 W, G! {
: i/ Q* i7 y% x9 M# N3 y8 c
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
% R7 \& Q1 @ }7 \# z{
6 j* F% |7 n& I5 P! N; {/ w0 p" H+ C. I7 y" Q
/* 建立数据结构 */
- R+ K/ D* s. X C0 Y+ j/ f9 z$ C1 l( W/ F% f8 a6 B8 J
struct mmc_host *host;& K% { C! n+ I5 t" J& L
5 w( b: _' c6 l; x8 p) p6 s* H6 _
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);; p5 Z+ g; ?" E9 P+ f
@4 ~0 s+ C+ o) l0 F, F7 W
/* 建立kobject */8 u8 M: e" ]9 I1 J
8 H) E! @! }" \0 g9 p host->parent = dev;
6 s Z6 g( _& e/ \ B# g' P host->class_dev.parent = dev;+ R* Q0 u5 Z: ~- e) l' L
host->class_dev.class = &mmc_host_class;
K$ O2 m. e2 W% W# O y5 _- C device_initialize(&host->class_dev);
5 u. b0 V( f: _. h0 [6 p5 u
& d8 h8 o+ u* s) y/* 初始化等待队列,工作队列 */4 B9 ]+ N! O1 S' s7 U2 X# {/ T" E
7 d9 j# n* I" |
init_waitqueue_head(&host->wq);
+ G; b# T7 L& e1 b) Y" ^ INIT_DELAYED_WORK(&host->detect,mmc_rescan);
/ K$ o9 d0 H* X! l
3 K. l4 f6 l' w) A1 v6 C; j ! i( G% O p8 S. S: ^+ F2 j
4 Z$ e/ F, R) d+ N3 }$ ]& g: }/* 配置控制器 */0 I5 b# `0 A# l# K
. B1 e8 P( M7 j
host->max_hw_segs = 1;
( A! _4 O. F2 }. i9 { host->max_phys_segs = 1;
$ i& n7 e; Y3 L: X" T5 F! q
" X, p; H+ m# J$ z2 R' z+ R' q ... ...
- \ s* Y: Q3 H4 @$ k4 _5 a return host;9 A" W6 N# J3 a
}/ X) P1 z. @+ g& U g1 J7 L
+ S1 X% `- o0 x
2) mmc_add_host3 C+ e" v) R$ F
' Z, `0 z( s' T/ n
完成kobject的注册,并调用mmc_rescan,目的在于在系统初始化的时候就扫描SD总线查看是否存在SD卡。注意到这里的工作队列的延时时间delay为0,因为系统启动的时候不考虑插拔SD卡,关于这个delay将在下文讨论。, ^8 \# S. ^. G7 M$ ]( p( u
. e, H0 y4 q! F4 D) c3 d* b' z! f0 X
int mmc_add_host(struct mmc_host *host)4 o7 G& K O# Y4 Z. y3 X% z& X6 w
{
4 f( s5 R8 K) v" n) B& k5 ` device_add(&host->class_dev);
! L; A9 ~# c9 x$ ]8 s {6 F4 ~ mmc_start_host(host);
) w2 u. |2 C9 T
' ]; ]! m& F% m$ w0 r" s' i+ c& m; K ... ...1 v1 t! k' ] |& N, ^* a' ^; g
" i& x( _( I$ Q$ e# O
}% O# t* t& W; t2 K- d7 c
8 z `2 _' Y" Avoid mmc_start_host(struct mmc_host *host)
G; u: ~$ }2 Z! r- `* L3 r& h{
) k* N- q3 Y3 X( h& L8 X- z mmc_power_off(host);
% T1 G0 l% P' }6 H$ j8 q& k mmc_detect_change(host, 0);1 @( [( M* b) e, U" j n, w
}3 k6 [4 b6 t) }, Q
& X8 v( x3 G: w void mmc_detect_change(struct mmc_host *host, unsigned long delay)' |5 ?( u9 ?* \0 B1 \
{; z/ L- J/ f. q+ b6 B8 ^& H
mmc_schedule_delayed_work(&host->detect, delay);0 H1 l+ `* S6 {# Q
}# Q1 ~8 _3 }9 `" [4 i: H
0 y, L4 m- h# _, X0 j static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) |+ y# }; c/ h6 |" @6 _; O
{
/ V9 z* a" ^2 @2 ]& {5 [% g6 Q wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ * 2);
9 R4 `5 b) I" Z6 G4 q) F return queue_delayed_work(workqueue,work, delay);
3 D* h8 S6 T( e- m9 k}7 P9 Q( N- P, u
6 _3 Z1 B+ {) s& r2 M2 ^
2.2.2 为设备赋初值
0 }( Z W5 u" |9 w! p! o 其实,整个设备驱动的probe()函数,其本质就是是为设备建立起数据结构并对其赋初值。pxamci_probe(struct platform_device *pdev)主要为SD主控制器完成时钟、存储等方面的初始化配置,8 n) J/ B5 v9 ^* A, A$ k+ g5 g8 @
static int pxamci_probe(struct platform_device *pdev)
3 {1 f9 M1 D6 s1 s+ i7 K9 e: }{- I% v4 ?" M7 q* F
struct mmc_host *mmc;+ l6 O: P1 r8 H( j, L0 V
struct pxamci_host *host = NULL;' Q' n" W0 f- {' }* X/ u* F. q; _
1 s& Q+ f3 w3 N, L mmc->ops = &pxamci_ops;
8 Q1 O9 l+ e* J: g; c* h1 |5 e mmc->max_phys_segs = NR_SG;
2 A: e8 s/ A% T0 v1 q2 Q- L mmc->max_hw_segs = NR_SG;4 z6 I- \4 R* g5 ^9 W" {) n9 B) \
mmc->max_seg_size = PAGE_SIZE;8 F6 p2 Q, s j* M% f. t
host = mmc_priv(mmc);5 P" [1 R6 M* ^' m W1 [
host->mmc = mmc;& p1 }8 I( g" B# M1 d4 Y* u' I* Y
host->dma = -1;8 c$ M$ m ?. l$ e8 @+ y
host->pdata = pdev->dev.platform_data;
% O0 A& H4 E- e, }# i: M: e host->clkrt = CLKRT_OFF;* {7 ?% O2 r. L; n
host->clk = clk_get(&pdev->dev, "MMCCLK");' U; |) z0 W- [5 u- N; v
host->clkrate = clk_get_rate(host->clk);
4 I5 O$ N* `+ Y- D7 D2 k mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
" W$ C% d @! c9 ~3 @0 ]; e! Q* y: ~ host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);# n. ^# f2 {8 Y) ` O9 R
host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, pxamci_dma_irq, host);% B9 U F. k1 V
... ...
5 i( @- Z5 [* g( o2 { Q}/ }. i% }# q, q6 S6 U; @
完成所有赋值后,通过platform_set_drvdata(pdev, mmc);将数据挂载到pdev->dev.driver_data。8 z9 n1 J) X0 e* X4 o: |
所有赋值中,我们重点关注从platform_device *pdev里得到的数据。platform_device *pdev是在系统初始化的时候扫描platform总线发现SD主控制器后所得到的数据。
5 `; w# ^! J' y V6 [- W2 J1) 得到platform_data数据! N* }+ C' F) O' y0 @- B
先看看platform_device的结构,
0 r- H5 q, P: j/ Z8 @struct platform_device {
: b) e8 q3 h: ~5 R7 _% V const char * name;
) G3 E4 x7 E- l% {! h7 e$ l int id;: j& M( [+ \4 _# F# D
struct device dev;6 t3 I" e7 | @$ K3 G
u32 num_resources;: ~) K H0 b4 _, |
struct resource * resource;
% K Y* H/ E0 C};- Y0 J+ S% E9 S5 c2 U6 x* Y
系统初始化的时候,已经为该SD主控制器的name, resources等赋上了初值,具体内容如下,
9 e- \, X, i' T6 Bstruct platform_device pxa_device_mci = {9 T' }( Q/ q7 d3 W
.name = "pxa2xx-mci",3 e# h) N$ Y$ D; Z
.id = 0,
# Z; s2 H# Y8 }7 r, _ .dev = {+ A1 H( K; b* I" O9 Z- Q' o, d
.dma_mask = &pxamci_dmamask,
' ]- y2 a7 f1 T9 a! r9 D .coherent_dma_mask = 0xffffffff,
* Y2 _; Y- |+ T( M6 G4 B, v$ f },
0 I2 i3 V7 A( m5 J .num_resources = ARRAY_SIZE(pxamci_resources),
) n9 ]; u3 b6 Z5 w) M .resource = pxamci_resources,
# [# f: I1 J9 [9 M};
3 e& N1 C% j% e/ [$ ystatic structresource pxamci_resources[] = {+ I* c7 j: ?6 o; ^6 J0 c8 M
[0] = {2 E1 { W1 S9 e2 Y( k. Q. V3 P
.start = 0x41100000,
+ `! e6 J0 u+ h .end = 0x41100fff,
! \: X. V1 f$ S' B" O1 i .flags = IORESOURCE_MEM, // SD主控制器芯片的起始地址
( B& S. [" O3 ^6 _! @ { },
6 w" P5 c" M' d: z8 Q `9 {% ~% k [1] = {
- [3 N% n6 h1 x8 z- P .start = IRQ_MMC, /* #define IRQ_MMC 23 */
y$ L1 J9 d0 p1 J, `' r$ R* }) i .end = IRQ_MMC,) R5 U% |2 a3 d7 {1 Y1 s
.flags = IORESOURCE_IRQ, // 申请的中断号
5 a* K- A+ t4 O! j1 I: m" C0 W- S },
& G! b ?% E( y) B ... .... V4 J+ Z) @$ X2 f$ a8 c/ N+ Q" F5 v
};
9 Y1 q3 G; h# i9 q# O 需要注意的是,platform_device数据结构里的name, id, resource等是所有设备都用的到的数据类型,那么设备自身独有的特性如何表现出来呢?事实上,结构体device专门准备了一个成员platform_data,就是为了挂载设备的一些特有的数据。(注意与driver_data相区别)$ R3 _; y, R# G
structdevice {
2 Q+ x; Y6 O) }5 b1 \1 p# N void *platform_data; /* Platform specific data, device core doesn't touch it */
5 o6 j1 J- A- v8 ` void *driver_data; /* data private to the driver *// e3 G0 y/ [" ~! O- k: y: a
... ...+ P7 A, k* `+ }/ V
}/ E8 p* b% j0 G% L. ~8 R1 h
看看SD主控制器为什么会有这些特有数据,
9 N# h" D& c7 Q1 n7 o/ ^- N5 u static struct pxamci_platform_data saar_mci_platform_data = {
2 }! O3 ^( a1 m" q1 K5 I7 { .detect_delay = 50,
1 g( P* L8 f- O& L S! s8 D .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,( O0 K* v H0 \
.init = saar_mci_init,
. l2 `$ d5 E+ i) j h .exit = saar_mci_exit,2 q* y5 @/ f* A) D
};7 w$ x" l3 K3 P
-> detect_delay
- ~: B; q7 ^3 K7 |0 C4 S' p 就是刚才提到的工作队列的延时时间,设置为50ms,由于各种SD主控制器芯片的性能不同,这个值可能会变化。那么为什么要为工作队列加一个延迟呢?首 先,当插入SD卡之后,SD主控制器上的探测引脚会产生一个中断,之后调用中断函数里的工作队列,然后执行工作队列里的mmc_rescan去扫描SD 卡,为SD卡上电,发送地址,注册驱动等。考虑到插入SD卡需要一个短时间的过程(有个弹簧卡 槽固定住SD卡),如果没有延迟,那么插入SD卡的一瞬间,SD卡还没有完全固定到主板上,系统就开始执行mmc_rescan,那么就很有可能在为SD 卡上电、发送地址的过程中出现错误(拔出SD卡同理),因此,必须要有detect_delay这个值。
2 d& h4 M* j+ c1 z$ J9 x1 d+ l& V) M5 I
-> saar_mci_init
0 P i$ [% j; k B. N这个函数为SD主控制器的探测pin脚申请中断,具体内容将在下文中断的一节中讨论。
0 f, J% X9 a9 O; c! Ostatic int saar_mci_init(struct device *dev, irq_handler_t saar_detect_int, void *data)! j. T/ g g+ F1 Z6 M9 ~: V1 |
{
9 r$ x& X" Q% `& ^' U4 Y9 L struct platform_device *pdev = to_platform_device(dev);- }% d' L4 W0 s4 R: t/ z! C
int cd_irq, gpio_cd; // cd - card detect
& f8 e3 s" b6 Q1 P; g- X 6 i2 m0 t d2 h; T0 A+ @! w. H
saar_mmc_slot[0].gpio_cd = mfp_to_gpio(MFP_PIN_GPIO61); // 将GPIO61设为普通GPIO口
( I/ a: ~& i2 i; j6 g$ c7 ^# b cd_irq =gpio_to_irq(saar_mmc_slot[pdev->id].gpio_cd); // 将GPIO61转换为中断号
6 o* w n* Q6 W& W2 o2 Q: ~7 Z gpio_request(gpio_cd, "mmc card detect"); // 申请GPIO61
7 ~) _* h: q; J. i% j9 Y gpio_direction_input(gpio_cd); // 将GPIO61设为输入类型
1 Z Q8 Y2 } L4 n" i7 R8 c8 t. N% S request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "MMC card detect", data);* ~: `* E% @. n& M
... ...
2 |9 o% b" {/ {$ f' Q$ K}
8 b$ H8 `2 M' l3 F3 `得到SD主控制器特有数据后,将其挂载到dev.platform_data下,并最终完成对platform_device *dev的注册。2 w/ P! U, @+ p
void __init pxa_register_device(struct platform_device *dev, void *data)
7 Y7 e Z$ P% E# m4 A. M H{( t' |. t- Y5 ^* O4 @1 \. q" h
dev->dev.platform_data = data;+ y% d2 {8 X8 U a7 J2 n% a
platform_device_register(dev);
4 x' r$ V6 H+ F6 y}
' n/ X, U2 i$ e- p4 x7 [/ k2) 使用platform_data数据
. }- V3 P: v- l1 U" L 下面就看看SD主控制器是如何使用这些在系统初始化的时候就已经得到的platform_device的数据的,5 t2 F$ d) G6 g
static int pxamci_probe(struct platform_device *pdev)
! G+ Y8 \3 O& t) F& I1 x- }{
2 G$ r9 E" z# _! e& P9 x( w" c struct mmc_host *mmc;
# X5 s& x7 E$ l$ T# z struct pxamci_host *host = NULL;
7 _$ n1 H# p. c9 I) j- c: l struct resource *r;
* e O) k. [/ l% { int ret, irq;& ?, A( G' b# c+ h: N" L
r =platform_get_resource(pdev, IORESOURCE_MEM, 0); // 得到控制器芯片的起始地址) ~' _# J1 U* t+ e
r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); // 为芯片申请4k的内存空间 o: B5 o4 U) V( T# S
irq =platform_get_irq(pdev, 0); // 得到芯片的中断号! n/ b' `- H+ W! E- }
host->res = r;. m$ W0 g' J. \( _3 f! K6 A
host->irq = irq;* \. A9 F# V Z1 |% Y1 G5 v
host->base = ioremap(r->start, SZ_4K);// 将芯片的物理地址映射为虚拟地址% m& F0 q J: N+ V4 l; ?
... ...* [/ p4 T9 X) m
}; H9 Y1 y. u% i9 N, i
6 m4 E" V4 d* t& P5 a2.2.3 设备驱动的功能函数' Z; j2 S" v( A# C( |
3 b0 x' f N: ~, u4 L6 R
一般情况下,设备驱动里都有一个行为函数结构体,比如字符设备驱动里的struct file_operations *fops,该结构描述了设备所具备的工作能力,比如open, read, write等,/ ?6 _1 }# T; ?; Q% r' Z6 W: e4 @
struct file_operations { q7 x* G2 X. ~3 O
struct module *owner;
$ }" ?7 i& C* ?) t ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);- z8 D6 v$ w/ N" P3 h7 F
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
4 u1 C- _0 e H" q4 b int (*open) (struct inode *, struct file *);& i# b2 G0 L4 v
... ...5 s/ X% C" T. D' ?
};
& b' f2 S) X, {5 Q9 h( w 同理,SD主控制器驱动程序里也有一个类似的结构struct mmc_host_ops *ops,它描述了该控制器所具备驱动的能力。
+ p$ B, |! d# `. @5 Gstatic int pxamci_probe(struct platform_device *pdev)
7 L9 `; H3 U: I2 ~/ P _4 |{$ S/ \7 ?; Y: G1 M' L
mmc->ops = &pxamci_ops;
2 Q) Z" o3 [+ J ... ...
. z. R5 A, C5 \: W9 t}4 U; v. o# Q2 o9 q. v( f. W
static const struct mmc_host_ops pxamci_ops = {. {7 ~! K+ u7 N: l
.request = pxamci_request,. {" r4 F" N; r: ~+ m
.get_ro = pxamci_get_ro,
' P8 k- k) ~3 X" W* E2 A0 s .set_ios = pxamci_set_ios,1 A! w( {" _4 A1 a, C
.enable_sdio_irq = pxamci_enable_sdio_irq,
4 N0 {7 Y$ V- T% P& o0 Y: \};/ m8 a) M; i9 `2 w* a" g2 k
其中,(*set_ios)为主控制器设置总线和时钟等配置,(*get_ro)得到只读属性,(*enable_sdio_irq)开启sdio中断,本文重点讨论(*request)这个回调函数,它是整个SD主控制器驱动的核心,实现了SD主控制器能与SD卡进行通信的能力。
- Y& Q+ E# T! ?% J5 }, ]& u- L3 |static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)' N9 N: [$ [8 \0 ]2 a2 V J
{
0 g$ Y/ S$ ]' l; d! c, g* I struct pxamci_host *host = mmc_priv(mmc); unsigned int cmdat;
, V: N: \5 |; w; a! a$ s" s 8 Q+ }3 X/ n& d
set_mmc_cken(host, 1);
* i; U+ h5 n( O: r8 B host->mrq = mrq;7 g7 W9 v8 E t1 |
cmdat = host->cmdat;9 ]) E) @4 ]# G! b. h
host->cmdat &= ~CMDAT_INIT;
1 Y6 h" H& `* ?/ @$ ^: F" u5 F6 Y4 N0 y if (mrq->data) {
, W$ S) l# K7 l, ?& v+ g pxamci_setup_data(host, mrq->data);3 [8 ~8 j& d0 r6 I' d/ Q2 @9 d# o
cmdat &= ~CMDAT_BUSY;
6 `; ~7 l8 S+ f0 W- ] cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
. x# |7 M8 P# d9 T% t+ t if (mrq->data->flags & MMC_DATA_WRITE)' \: v: T N1 h6 D9 a2 w
cmdat |= CMDAT_WRITE;
* [& K k* g6 c. W if (mrq->data->flags & MMC_DATA_STREAM)
' b& v; z# [& ^9 A$ a* v cmdat |= CMDAT_STREAM;6 w" V( l1 G% k' {3 B
}- v# ~7 h8 M5 c' J) m
pxamci_start_cmd(host, mrq->cmd, cmdat);
* h! m: f, \$ {; o}
& f7 Y/ i/ r% p8 I) r其中, pxamci_setup_data()实现数据传输,pxamci_start_cmd()实现指令传输。& l' z! a- I6 i0 p
至此,我们必须去接触SD主控制器的芯片手册了。) b" U& G( C& W/ a$ e. [
e" a: k& t8 _( D5 L% h$ z. H首先,SD主控制器由一系列32位寄存器组成。通过软件的方式,即对寄存器赋值,来控制SD主控制器,进而扮演SD主控制器的角色与SD卡取得通信。0 W- ?/ O) S8 l$ `- ?5 C" u
/ N2 r! T/ g& v; q3 [/ ^
1) cmdat
+ u9 o; j- w, z! V5 Q 根据主控制器的芯片手册,寄存器MMC_CMDAT控制命令和数据的传输,具体内容如下,
. T) T9 Q+ z' [
7 [3 _) I( Y2 m2 ~+ j, S3 C. s结合对寄存器MMC_CMDAT的描述,分析代码,9 U. ~. L9 O" o' K: T
host->cmdat &= ~CMDAT_INIT; // 非初始化状态
W9 s. d$ k8 b- O9 T% d if (mrq->data) { // 如果存在数据需要传输! ?: H: o/ O: N
pxamci_setup_data(host, mrq->data); // 实现主控制器与SD卡之间数据的传输1 q4 E1 @4 p; z2 |
cmdat &= ~CMDAT_BUSY; // 没有忙碌busy信号; N7 D4 M# p7 o3 H
cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; // 有数据传输,使用DMA0 w! r% `0 j$ O# v: ]' t2 Q* h# D
if (mrq->data->flags & MMC_DATA_WRITE) 3 ]9 ], \3 t) }5 R! B: N: N9 \/ @; E
cmdat |= CMDAT_WRITE; // 设置为写数据
# t- ^6 ~* P5 \% t+ s# |! W8 | if (mrq->data->flags & MMC_DATA_STREAM)
6 ?1 V4 H+ b3 c( s cmdat |= CMDAT_STREAM; // 设置为数据流stream模式
/ V V# i! @! u. X+ i' e1 S}
3 S: Q9 P. e$ `! }* L# s2) pxamci_setup_data 通过DMA实现主控制器与SD卡之间数据的传输
O! P6 W/ x' f* Z9 ]9 rstatic void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
8 R6 N" O$ S+ K! U( k j: A{+ } ^" T: L8 T4 i- O
host->data = data;
9 N) D4 E# O+ ]; l1 G! ^& r$ m writel(data->blocks, host->base + MMC_NOB); // 设置块的数量( J& k7 `6 @& [" p: M& S+ t" y$ b
writel(data->blksz, host->base + MMC_BLKLEN); // 设置一个块的大小(一般为512byte)9 X; V) _, N& g, z- Y
if (data->flags & MMC_DATA_READ) {
$ H9 Q# B& X, y* a: q host->dma_dir = DMA_FROM_DEVICE;
# u2 L; E# [2 Q, B L* A. z dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
( Z0 R* D0 b! C7 ~% I1 m7 H, L/ g DRCMR(host->dma_drcmrtx) = 0;
) e0 t' i. D: a3 J" n DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD;) d2 \/ c) f! Y, d9 W! ?7 l
} else {
' ]" g" m' L" m; b host->dma_dir = DMA_TO_DEVICE;
' J1 K. z- ~3 j* d; o dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;5 C6 Q p( t, R; ^) `* i
DRCMR(host->dma_drcmrrx) = 0;) r" Z ^4 ?6 j, c( [% ~1 b7 }
DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD;
9 D6 e& M( s4 ]. h# i }8 I- ~2 W8 k: |/ [
dcmd |= DCMD_BURST32 | DCMD_WIDTH1;$ E6 \- m5 k! H0 c% E @
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);$ I/ g: }2 Z4 M. g e! r! s
for (i = 0; i < host->dma_len; i++) {6 L; `: n6 {9 q* b' x. \8 ?
unsigned int length = sg_dma_len(&data->sg);
6 e; }1 y, L' M& V( n% l host->sg_cpu.dcmd = dcmd | length;" G+ _/ l$ i W0 ~; s4 c5 ?
if (length & 31)& C1 ]. _4 O5 y# }- e: r* v
host->sg_cpu.dcmd |= DCMD_ENDIRQEN;- s3 W/ _; r; X6 T m7 Z
if (data->flags & MMC_DATA_READ) {8 e+ J; m; e7 ]3 p' j
host->sg_cpu.dsadr = host->res->start + MMC_RXFIFO;
# B, V' Q( D$ |) f host->sg_cpu.dtadr = sg_dma_address(&data->sg);
2 z- Y( ~% P! [2 L/ v } else {' p- {2 ?' G- i5 D
host->sg_cpu.dsadr = sg_dma_address(&data->sg);$ W- j3 V. _7 b* V% X( G
host->sg_cpu.dtadr = host->res->start + MMC_TXFIFO;
( H" J( X- l8 K: }0 U }; ~1 f+ j. U9 B5 G
host->sg_cpu.ddadr = host->sg_dma + (i + 1) *( @! s7 L% c6 A9 X/ e
sizeof(struct pxa_dma_desc);
0 U3 t+ H# g8 Q; d$ |: w: K5 D }
$ G. q$ s7 S+ _1 j/ H W host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;+ D$ S! \/ W$ W1 p5 e; x7 m7 T
wmb();
8 L5 s; s7 S) n2 I) A DDADR(host->dma) = host->sg_dma;
* D2 c- Y E* `; D2 h6 ^2 v DCSR(host->dma) = DCSR_RUN;# G' m" b+ T' _
}; H. t4 W: u/ ?# x5 K
for()循环里的内容是整个SD卡主控制器设备驱动的实质,通过DMA的方式实现主控制器与SD卡之间数据的读写操作。& n0 q' t4 P& H! ?5 ]8 P
3) pxamci_start_cmd 实现主控制器与SD卡之间指令的传输/ g* T$ t. I1 ]4 @4 B
static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
1 l( s3 g0 Q6 s* @1 E, x. w/ O{
! p7 I% a+ y: M2 j- B a host->cmd = cmd;' j' D* s# {* E$ n# b' R
if (cmd->flags & MMC_RSP_BUSY)
0 R5 @1 C* j/ \3 ~3 P& ~4 l cmdat |= CMDAT_BUSY;* T+ W/ S! v4 e! n- R2 f
#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))6 [, k" t/ J& \7 ~9 a) q* _) O
switch (RSP_TYPE(mmc_resp_type(cmd))) {& E7 B! b+ y8 o6 k) N$ e0 ?6 ?
case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */ @! D( V1 r% q( l, u/ Z
cmdat |= CMDAT_RESP_SHORT;
( } H1 w& y/ d1 C9 s0 T" l X; X/ G break;0 q1 t. K# |/ A! T
case RSP_TYPE(MMC_RSP_R3):
, r; [ M2 Q9 I cmdat |= CMDAT_RESP_R3; w3 E; D1 ]: x" n! Z
break;/ G; Y A( p# A1 f
case RSP_TYPE(MMC_RSP_R2):* m' y, [5 n3 i
cmdat |= CMDAT_RESP_R2;
( N" g) z+ i; h% I% p3 }6 U break;+ {# q7 p# B$ |, P( u' }% r5 R
default:
7 ~3 ?1 v# _1 @6 z break; M7 [- A( V3 e9 F2 Q
}& M' l w. k9 A5 {7 M; T! W; E
writel(cmd->opcode, host->base + MMC_CMD);
* p7 c* n7 Z; M% v, l writel(cmd->arg >> 16, host->base + MMC_ARGH);4 L- L1 S9 V1 z7 D/ m
writel(cmd->arg & 0xffff, host->base + MMC_ARGL);5 Q# g, H$ G* m. G0 V) d
writel(cmdat, host->base + MMC_CMDAT);
) {$ z2 o# u7 z+ O* m; A" c5 X pxamci_start_clock(host);
/ g7 o+ M# B7 F pxamci_enable_irq(host, END_CMD_RES);
) \" d3 |4 A0 g, o3 C8 X1 z. ^8 t}% Q! n& e0 A" i1 y
-> response类型# a# o/ G' k" i8 l! F$ @" Z
根据SD卡的协议,当SD卡收到从控制器发来的cmd指令后,SD卡会发出response相应,而response的类型分为 R1,R1b,R2,R3,R6,R7,这些类型分别对应不同的指令,各自的数据包结构也不同(具体内容参考SD卡协议)。这里,通过RSP_TYPE对 指令cmd的opcode的解析得到相对应的reponse类型,再通过swich赋给寄存器MMC_CMDAT对应的[1:0]位。3 R9 T! x3 ?4 [/ C: H0 J, l
-> 将指令和参数写入寄存器; G; } d2 P5 {8 S8 i5 L7 f
4行writel()是整个SD卡主控制器设备驱动的实质,通过对主控制器芯片寄存器MMC_CMD,MMC_ARGH,MMC_ARGL,MMC_CMDAT的设置,实现主控制器发送指令到SD卡的功能。
* X: e9 W& R( x' B* [1 }4) 调用(*request)
1 ?+ S5 J4 F# Y+ i$ G; N 以上通过pxamci_request()实现了主控制器的通信功能,之后只须通过host->ops->request(host, mrq);回调函数即可。! Q: r! y3 u$ g+ S. e
协议层里,每条指令都会通过mmc_wait_for_req(host, &mrq)调用到host->ops->request(host, mrq)来发送指令和数据。
' j: _* q5 x5 V; T, T: b/* core/core.c */
k: V. a7 J% g- }1 ]0 h( ?void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
9 d5 Q6 T% ]$ I% L{
_9 B: C d; a2 r. J0 l DECLARE_COMPLETION_ONSTACK(complete);
8 t9 u& w* o+ S$ { mrq->done_data = &complete;! l( A! O. }3 `. J- o1 U
mrq->done = mmc_wait_done;
4 u# Z0 J+ m3 h; x mmc_start_request(host, mrq);* i' g: f. y* `' m* X w
wait_for_completion(&complete);, N- R8 w, _2 R- O, ?( A$ u
}' E9 a, N' Q! A7 ]: W
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) Q, }8 C5 S, U [# r' W& ?: j5 U
{3 U" H' f$ V2 L: t- Z/ Y
... ...
* [* ]6 G+ [/ N5 v4 D5 V host->ops->request(host, mrq);4 w, G. ]" x9 B U. ], [5 e
}
/ r- b7 G! ]3 c6 j9 Y6 x4 x( S/ }9 R& h
2.2.4 申请中断! Z- O: I5 ~2 b8 F
pxamci_probe(struct platform_device *pdev)中有两个中断,一个为SD主控制器芯片内电路固有的内部中断,另一个为探测引脚的探测到外部有SD卡插拔引起的中断。5 h! L$ |; H) \7 x8 h
1) 由主控芯片内部电路引起的中断
) e/ ^6 k/ s. Z$ X% k% l request_irq(host->irq,pxamci_irq, 0, "pxa2xx-mci", host);- f% k- Y- l" [3 q3 |
回顾一下,host->irq就是刚才从platform_device里得到的中断号,
7 n2 @; l8 O! z4 O0 @' C irq = platform_get_irq(pdev, 0);# x3 f! c( y8 I) F5 i
host->irq = irq;
+ a: y! Y8 g: d2 b0 c5 ^* h/ x接下来,pxamci_irq便是为该中断host->irq申请的中断函数,& W& D7 Q# h! G# m3 j6 I
static irqreturn_t pxamci_irq(int irq, void *devid)/ m. m3 w g, ]2 _" f& H
{' ~% z. o q3 q8 I9 c6 w: j
struct pxamci_host *host = devid; // 得到主控制器的数据
' o" U+ K& [. r) f3 |& ~, k! Y unsigned int ireg; int handled = 0;6 r0 t% _1 t+ m- N/ Y, S: l
ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK); // 读取中断寄存器的值
7 T0 X4 R1 ]6 c$ F7 c if (ireg) { h8 l6 G: K) E
unsigned stat = readl(host->base + MMC_STAT); // 读取状态寄存器的值; f$ [% l6 ~$ d% x0 |1 r2 Y8 E
if (ireg & END_CMD_RES)/ T5 s4 |4 C( K }2 a, \& ^
handled |= pxamci_cmd_done(host, stat);" g; a, C5 h! O% a6 F6 }
if (ireg & DATA_TRAN_DONE)
# J2 R& q9 w: h" I) e' o handled |= pxamci_data_done(host, stat);
6 _& k5 s( n$ D9 q4 \ if (ireg & SDIO_INT) {
+ {" o& {0 K" }) f6 g mmc_signal_sdio_irq(host->mmc);! d1 s1 _6 ]; `; e8 e1 c
}3 B. Q7 |3 ]! D% P9 i+ V# K/ A* [; x
}; J- p+ n$ ]5 O0 P: @
return IRQ_RETVAL(handled);+ {: |: p6 L1 v
}& B" w( u0 c# S" p6 t
当 调用(*request),即host->ops->request(host, mrq),即上文中的pxamci_request()后,控制器与SD卡之间开始进行一次指令或数据传输,通信完毕后,主控芯片将产生一个内部中断,以 告知此次指令或数据传输完毕。这个中断的具体值将保存在一个名为MMC_I_REG的中断寄存器中以供读取,中断寄存器MMC_I_REG中相关描述如下,3 e# r- a7 g1 r9 L! |/ f
- Q( w5 O! b* c4 Y' R如果中断寄存器MMC_I_REG中的第0位有值,则意味着数据传输完成,执行pxamci_cmd_done(host, stat);
8 {/ f8 c4 x5 y. {如果中断寄存器MMC_I_REG中的第2位有值,则意味着指令传输完成,执行pxamci_data_done(host, stat);( X" l1 f4 R9 h* E' }! G
其中stat是从状态寄存器MMC_STAT中读取的值,在代码里主要起到处理错误状态的作用。9 j- M0 T' R6 H1 `
-> pxamci_cmd_done收到结束指令的内部中断信号,主控制器从SD卡那里得到response,结束这次指令传输
; K6 \( @% X: q# Q4 b# p% e# }这里需要注意,寄存器MMC_RES里已经存放了来自SD卡发送过来的response,以供读取。
( F# O% B2 q4 @1 I9 K& n8 R$ C6 Ostatic int pxamci_cmd_done(struct pxamci_host *host, unsigned intstat)
; t' C0 A2 \ Y& H$ o{
1 r- @% n/ A6 ^7 [) D' K struct mmc_command *cmd= host->cmd;
0 `+ {$ u0 S. T1 i1 @1 A cmd->resp= readl(host->base + MMC_RES) & 0xffff;$ [7 c; o& d. e9 g' {9 X0 d2 m- g
if (stat & STAT_TIME_OUT_RESPONSE) {
) S! o& n6 B# B" \, }5 W6 N cmd->error = -ETIMEDOUT;, {9 `% t/ N/ c7 ~% S4 [: c! T3 e% Z
} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
2 s* U5 H# i" }8 n) ~ cmd->error = -EILSEQ;
, _7 X/ @+ U8 }% A( O# s}
, p7 M0 Y3 W: ^ pxamci_disable_irq(host, END_CMD_RES);1 W' Y0 g% h* A4 b
if (host->data && !cmd->error) {9 D" z( K# I; q# N8 w; W3 B: A
pxamci_enable_irq(host, DATA_TRAN_DONE);; S3 \0 g0 l/ ~
} else {( m% g' u \& x( h- u4 j& O
pxamci_finish_request(host, host->mrq); // 结束一次传输,清空主控制器中的指令和数据' R3 H) K* {9 L3 q1 F1 Q( V
}! x) R; ?, B2 m5 b3 H1 g& p
return 1;8 v6 S1 N5 M5 N; t+ X
}
6 M8 H% w0 y" N# _9 H8 o-> pxamci_data_done 收到结束数据的内部中断信号,得到传输数据的大小,结束这次数据传输
2 _& O0 g& E4 g" M9 @" `: N static int pxamci_data_done(struct pxamci_host *host, unsigned intstat)) `3 o: M4 y+ c4 C( S- Q4 j/ i9 L
{6 t$ @* I- r5 x4 v1 m
struct mmc_data *data = host->data;( R( Q' J, K+ e! J* K0 k
DCSR(host->dma) = 0;
' p% B) b3 D" H0 B9 T2 v dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, host->dma_dir);' \# R. k9 _, S- ]0 N# Y% T' q
if (stat & STAT_READ_TIME_OUT)
- M ]; Y1 X7 I4 T- h+ V% Z! T& @) z data->error = -ETIMEDOUT;
& ~9 }/ V' b! g6 Y* w3 s# z& u/ k! i. T else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))- V' a+ |: S6 a. {( L
data->error = -EILSEQ;( U) }) R+ r( F0 }: ]7 d
if (!data->error)) G- O4 J2 S+ H
data->bytes_xfered = data->blocks * data->blksz; // 数据传输量=块的数量*每个块的大小(一般为512字节)
; T& G2 M! v! j& ? else3 K! ]% \- x% B
data->bytes_xfered = 0;
& @* A/ K( b+ ^2 y' j4 _# ^" l( @ pxamci_disable_irq(host, DATA_TRAN_DONE);9 y: A, a5 Z, Z6 T
host->data = NULL;
3 N6 U9 s2 S3 }2 T- a pxamci_finish_request(host, host->mrq);
+ V @- V: U0 B3 |9 U# ?* n ... ...
9 ^7 R1 [- ?& o. E}
2 Z" J Q$ @6 \/ B3 H. K* Istatic void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)& M* d7 C+ y# R
{
7 A/ v9 i5 m6 d0 F m1 j host->mrq = NULL;" |0 l6 y$ m8 b8 @& x2 o
host->cmd = NULL;
) x% M! {" W6 S. V. ]% T6 | host->data = NULL;6 D& w* D ^- Z4 E- E: f( g
mmc_request_done(host->mmc, mrq);
' e/ o' O0 m8 X8 F+ L4 m set_mmc_cken(host, 0);
" |8 C( Y; O0 W$ x' a- R$ n unset_dvfm_constraint();
2 |+ g l, S5 d p3 w E" {}
: x! S6 B# y7 v |/* drivers/mmc/core/core.c */
; C% S X" u' S8 r6 `void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)9 l' e& S3 O3 Y- v# D% X9 a
{! h$ k2 U# d Z* b) L
... ...6 @2 K0 I/ h7 i Q9 z% y U
if (mrq->done)
& M& `" Y z" J mrq->done(mrq);/ @4 a8 r% ~- h/ J3 {
}
$ ]7 K, k. z) w% m% s$ z2 A这里用到了完成量completion,LDD3上是这样说的,完 成量是一个任务的轻量级机制,允许一个线程告知另一个线程工作已经完成。这里的一个线程就是内部中断处理函数,它是结束主控制器与SD卡间通信的线程,通 过mrq->done(mrq); 即complete(mrq->done_data);告知另一个线程-回调(*request)实现主控制器与SD卡进行通信的线程,通信已经完 毕。9 B @. i4 N% B+ q+ u8 a
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
% H5 Y4 `4 r) G# P1 _0 Q{; ]! M% F& F# \. p7 k% A E
DECLARE_COMPLETION_ONSTACK(complete);6 C) y! ^# f% ]& t, X( o
mrq->done_data = &complete;
3 j1 W: O K! E mrq->done = mmc_wait_done;
3 J- W' W, [& Q, S0 [& `9 U mmc_start_request(host, mrq);
$ S* t0 R: b: k0 r7 v& X2 ]/ L wait_for_completion(&complete);, x4 y# D8 a& c8 V( p6 M i i
}$ v% p* ^% m5 h( Q d, ^+ Y
static void mmc_wait_done(struct mmc_request *mrq)) f5 a5 _% X8 t" K4 ^# v
{+ T7 p; b) n7 H0 f
complete(mrq->done_data);* n* u% B8 g$ B. C8 U6 }
}
0 x! Y( m K N% e7 U# i+ h. B2) 探测引脚引起的中断
8 I5 k, a0 |% g4 L
& a: `2 S; {# q; ?) ^" d if (host->pdata && host->pdata->init)
$ b9 M5 C2 o5 n* h% } host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
; E+ m! {# b6 N9 w( c$ y 该init()回调函数就是刚才提到的系统初始化时通过saar_mci_init()实现的,
& L) z; i u2 y: \static intsaar_mci_init(struct device *dev, irq_handler_tsaar_detect_int, void *data) [! [9 J/ Z4 M6 K( O2 H7 e& U
{; f- X) q, Q% V* D
request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "MMC card detect", data);% t- C4 h5 Y3 b
... ...! o3 m2 |3 g% K. B. x- L
}3 N$ g) j- x0 Y" b; `
其中,cd_irq是通过GPIO转换得到的中断号,pxamci_detect_irq便是该中断实现的函数,之前已经提到过mmc_detect_change,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数。
- ] ]; f: @ q1 z3 kstatic irqreturn_t pxamci_detect_irq(int irq, void *devid)0 I, r4 d9 o: ~; x0 `
{
6 l' N7 N3 ]/ v# Y struct pxamci_host *host = mmc_priv(devid);
: h6 s4 @/ m/ h2 i# Q mmc_detect_change(devid, host->pdata->detect_delay);! [( _& v( N8 n! t4 g- o
return IRQ_HANDLED;
! t- c' @+ [ w& v; q}5 f& G$ {# B) u0 i, a7 c
当有SD卡插入或拔出时,硬件主控制器芯片的探测pin脚产生外部中断,进入中断处理函数,执行工作队列里的mmc_rescan,扫描SD总线,对插入或拔出SD卡作相应的处理。下文协议层将讨论mmc_rescan()。
# a* @+ r7 ]/ N0 G3 k% d! {
* h, V$ b' X9 a( ]9 S M9 l2 S) w 3. 协议层/ U& X' e2 S; l
( f. t4 b9 ]7 C+ U. `8 y' W Linux在内核源码的drivers/mmc/core文件夹下为我们的提供了一系列SD卡的接口服务函数。可以查看Makefile如下,
" f: B# r2 I$ V- P+ `. ?
9 p. l0 l4 W% L/ v) h% {可见,core文件夹下有针对总线的服务bus.c,针对主控制器的服务host.c,针对SD卡的服务sd.c, sd_ops.c等等。
/ S* V1 n/ S, U% w. [. {1 `
% d% S# U8 ]- r+ h9 t- [) P其中,最为核心的一个函数便是之前提到的位于core.c的mmc_rescan,概括来讲,主要完成两项任务,即+ c0 J' X8 ^! e. X
) F6 a! u$ N: F( q: X1 X
- 扫描SD总线,插入SD卡
- 扫描SD总线,拔出SD卡% Z1 e( n+ g; B6 W* M( c- v3 j
1 j, E: M. d3 S( w, }/ R( F9 l) v9 [4 v; h) D: w
3.1 插入SD卡/ c8 Q1 z/ a8 E9 ~
; C+ r( c+ Y0 D6 G插入SD卡,主控制器产生中断,进入中断处理函数,处理工作队列,执行mmc_rescan。8 u; D% n7 x% q/ K x+ n
# u$ A5 H7 ?4 G7 u* F; Gvoid mmc_rescan(struct work_struct *work)
" I# L, |3 F3 Y- D3 ?{& ?9 V7 }* ?3 x' X
struct mmc_host *host = container_of(work, struct mmc_host, detect.work); // 得到mmc_host的数据
6 Z' E: r* A7 s+ e2 ]2 q /*
0 W3 K, R9 N- i5 O; Z * First we search for SDIO...
/ p. ^: X% T2 U: y. Z */
! ^7 {+ A% a& d) i err = mmc_send_io_op_cond(host, 0, &ocr);
" C1 G( j* c! q! n3 F$ D if (!err) {
* ] F2 N e+ Q/ R4 d if (mmc_attach_sdio(host, ocr))
0 ~6 v) ]3 z0 \. e/ a mmc_power_off(host);. a7 T1 D0 G( `
goto out;/ E- @9 L" g+ g% g! M
}9 W9 u8 d1 e- g, l5 \" E* z
: r. t- N) S3 S: h$ O, A
/*) s. o8 Q ?$ Q0 |0 ~- U
* ...then normal SD...
4 R8 t& l' @+ `6 b */
5 D6 M$ b8 U/ l err = mmc_send_app_op_cond(host, 0, &ocr);
y5 G; ~3 T2 f/ D% T if (!err) {! X c, Y8 ^0 B5 L8 ]
if (mmc_attach_sd(host, ocr)); ]2 D. k/ I9 e
mmc_power_off(host);! F+ i8 ~# P0 A0 b
goto out;
9 @. V, F0 k" k% e& f- b }$ K% u& S" B5 y1 B0 L& j' Z9 Y
' K( }4 ^2 U' E8 t1 b /*5 g2 o3 A& ~6 H& B* L
* ...and finally MMC.; l+ j, u9 y% |; X7 X# m6 P0 {
*/
# p# i; ~7 Q, I. x err = mmc_send_op_cond(host, 0, &ocr);5 _! M& R. x( d- z! |( T
if (!err) {
7 Z4 Z. V" b' ^" ~0 [6 e if (mmc_attach_mmc(host, ocr))1 o3 b" @7 Q0 j- Q$ D# I6 k9 B
mmc_power_off(host);9 Y2 Z p0 W$ G
goto out;
' A% G- |0 N( u- ] }* B/ L+ ]+ | F% {( l# X
" ]0 I" F$ D+ G( j: _4 P5 U ... ...( A2 f# D5 Z6 t8 k2 Y! j
( [* K7 N9 x3 r8 r+ ?3 q}
( @+ x# o) l7 M0 L8 |
3 ^2 _$ h" S: ?# g1 [: U0 J( F) i插入SD卡,mmc_rescan扫描SD总线上是否存在SD卡,具体的实现方法就是通过向SD 卡上电,看是否能成功,以普通SD卡为例,为普通SD卡上电的函数mmc_send_app_op_cond(host, 0, &ocr);如果上电成功,则返回0,即执行if()里的mmc_attach_sd()进行总线与SD卡的绑定。如果上电失败,则返回非0值, 跳过if(),尝试其他上电的方法。那么,上电方法究竟有何不同呢?具体看看mmc_send_app_op_cond()的实现过程,: V8 k7 ?! b N5 ]! J, ~4 w
$ V9 h, H( R( d" i; s1 X! b; q
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
4 u% U' @- J0 u" ~; A{% M* K' j7 ^% K& ~0 f7 q
struct mmc_command cmd;" c% d$ x5 A) R! m% t# V
cmd.opcode = SD_APP_OP_COND; /* #define SD_APP_OP_COND 41 */
) m) A; v$ Y( ?: T. `0 v mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
' x( j, c4 f2 [( e9 d# ?* B, `% W; r0 i1 |1 b
... ...
/ \) i. S1 v8 o7 o3 c( t) C G4 a6 k M8 O
} p( H1 Q+ k$ V' I4 ? S
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries)
1 H) v0 e/ E7 l{
Y$ n6 p: O& Q, y% e: U, a4 z; d! e( q: K1 q, G
mmc_app_cmd(host, card); /*#define MMC_APP_CMD 55 */& J" p' b/ q% t1 H7 y1 N; D
mrq.cmd = cmd;; [: U6 \8 |$ \# }6 k5 f% `9 D
cmd->data = NULL;8 A& O! q/ K7 C; l' I3 x
9 l4 @8 S) ~; G1 ^: c mmc_wait_for_req(host, &mrq);$ D4 k" w1 ?# B* a" q3 r5 n
. x9 ^0 ^9 e; s4 W
... ...
% i4 G: i0 v3 f3 v- P8 O, @
) s# K! B" D5 [* p! A; I}# T/ w7 @% [2 D
" h. K/ R+ d2 T" n" Q* U这里的指令SD_APP_OP_COND只有SD2.0的协议支持,也就是说,只有普通SD卡支持,所以也只有普通SD卡能够成功上电。 9 ], E# R0 s* C. {
" }: n# `# t1 P0 ~. F" v" ?2 o
如果上电成功,就开始进行总线与SD卡的绑定,通过mmc_attach_sd(),绑定过程可分为四步,7 w% ~( Q) s. R1 `! M& ^
! E$ |7 M7 w" ^/ }注册SD总线上的操作函数 - struct mmc_bus_ops mmc_sd_ops; k( e; @' H6 D: e
设置主控制器的时钟和总线方式 - 通过回调函数host->ops->set_ios();1 e! G8 l5 S5 B% U, M
启动SD卡 - 根据协议,完成SD卡启动的各个步骤
& u5 h1 a+ b) o9 J6 W1 d: ~注册SD卡设备驱动% E3 E* x, T; V* _
* |! ?3 v5 r6 t# L
3.1.1 注册总线上的操作函数/ Y' h% j7 U* k1 E' J* K6 P
5 ?( e# l; J+ @) }
int mmc_attach_sd(struct mmc_host *host, u32 ocr)9 Q9 M0 ^: q# Y0 g q
{9 t$ r# {" J K* {4 o6 ^+ \* i
mmc_sd_attach_bus_ops(host);7 R n! J" _) X, ?( |
" `2 `7 Z: q$ J E: b. ` ... ...; s8 E3 }* D7 j. D2 j( `2 D
8 R! G& x! p, N, i1 f8 R4 d
}
6 J! M5 u+ z, u0 o/ i5 w/ e( d$ `! o/ G+ f$ {
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
0 h, T8 |/ J! Z u1 K{
7 y4 D$ b* D/ x! | const struct mmc_bus_ops *bus_ops;
5 ^ C% y4 x; ]% ?/ C( H* t7 ^+ E0 n) ?" n* @4 r: u
bus_ops = &mmc_sd_ops;+ n4 O* P7 w- I) w; z' M8 q; s
mmc_attach_bus(host, bus_ops);
+ `2 [2 Q1 l1 m, U. [5 X2 Y}5 q( v& N- z4 U" x7 w
4 L: N" J( a+ A5 v9 J
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); C. u4 U1 Y+ F* q
{# z* l" g6 M5 h# m: G/ J( s
host->bus_ops = ops;
3 F" V$ P' v k4 T( b# W ^- }. E host->bus_refs = 1;0 j& J! w" b( ?- ?
host->bus_dead = 0;; n+ ]% O% K9 S2 e" y4 m; p
# H' A7 u* b3 H# f( ]}4 G6 ]5 m9 |6 c0 M8 [% ^; J6 o
! F% n/ b N) {) E& lstatic const struct mmc_bus_ops mmc_sd_ops = {
, c( Z" w- O5 ?% Z% z1 ~ .remove = mmc_sd_remove, // 拔出SD卡的操作函数
4 I6 k/ F2 f' C) h .detect = mmc_sd_detect, // 探测SD卡的操作函数
0 o7 r' q# p. s) P% A; w8 _ F+ @ .suspend = NULL,: i9 ~4 i( g/ I3 F. m& j' ]! J
.resume = NULL,4 J4 Y0 Z' V# A a, j
.power_restore = mmc_sd_power_restore, // 重新启动SD卡的操作函数
8 L4 t( C. B# W" `1 ?$ E};/ v$ e9 w+ ]3 M( J6 F5 X5 \
这里的mmc_sd_detect和mmc_sd_remove就是拔出SD卡所需要用到的函数,下文将详细讨论。这里需要注意的是,插入SD卡的时候, 并不执行mmc_sd_detect和mmc_sd_remove这两个函数,但是会注册它们,也就是说,这两个函数的功能已经实现,将来可以使用。
: U7 Y3 Q6 D+ F9 ]) a/ c' }. {7 o+ L8 A1 ^. L- c$ A& m. `
3.1.2 设置时钟和总线4 U7 g* d) J/ ~, J3 _4 j4 p H
# i9 S) o% p/ J; J int mmc_attach_sd(struct mmc_host *host, u32 ocr)7 g# L3 Y7 o |0 H6 \* `
{
4 m% y) m" o' I* C8 Y host->ocr = mmc_select_voltage(host, ocr);7 e; `( D2 w6 J1 q+ D
. e5 H' |; z) y+ k: U7 b8 X7 ^
... ...
4 M3 q8 u- O" e" ?
* b* r5 {/ I2 m6 {# ?1 ~}+ H& T2 [3 h) o/ x
: {, X6 |2 H, O) U5 H( I
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
' \) H4 L3 v G/ ]% \- b4 }2 m{
8 u. a' n2 `4 l mmc_set_ios(host);- ~/ `2 E9 I. m9 |6 U5 J8 [7 b4 v
* l |& }$ b2 {$ c4 F
... ...
- L% T7 r6 M5 J! h+ ^}
) ^4 b4 o0 @6 I) S% ^* e7 Xstatic inline void mmc_set_ios(struct mmc_host *host)- E9 V j. U S! D
{: j$ _# i- E% z6 m& h
struct mmc_ios *ios = &host->ios;
) ]& m' a1 z. C( H: j9 z' R+ m* {# ^8 P
host->ops->set_ios(host, ios); // 设置主控制器时钟和总线的回调函数,具体实现由主控制器驱动完成
6 `, L; I/ V7 W' p}
: m% C& A4 p3 v8 V从这里可以体会到回调函数的精髓:协议层里利用回调函数为所有满足该协议的设备提供统一的接 口,而具体实现由底层不同的设备驱动各自完成。注意到,之所以要定义一些放之四海而皆准的公用的类,比如struct mmc_host,就是需要通过struct mmc_host *host指针作为形参传到协议层所提供的接口函数中,从而得以调用。( Q* ?4 {/ y% h' Z
! y9 T1 N' _# k* r1 ~1 S
3.1.3 启动SD卡# e( ` W3 h! q0 y4 K4 @; x' \5 k0 Q
8 A4 e- i- _7 L( @ H& o/ o8 Z
int mmc_attach_sd(struct mmc_host *host, u32 ocr)% s0 `. f/ H- L) E0 c
{
5 `: j4 e+ M+ U. g: }1 {- K# E" O; F. M
* I# W( A$ f- ^* B) I% I! a5 G mmc_sd_init_card(host, host->ocr, NULL);( [' K$ K" a. T" |6 W
+ M& D" Y0 t% Y, O
... ...8 a5 l# \7 I+ W. h2 |) i" P- N
6 i8 b( Z/ B: h7 J+ \}
- q( }& L& v- J8 z0 y, N7 u" @, D8 b- K- A$ F9 T7 x
mmc_sd_init_card主要完成以下任务,
2 a1 O/ @; U' d, q% L9 u' ?0 e) |& M
- SD卡的启动过程
- 得到寄存器CID, CSD, SCR, RCA的数据
- 其他操作比如切换到高速模式,初始化card
$ w, M. c4 u9 G+ i9 K7 Z ! O# _7 \4 e7 N' x% h2 a
7 X" X1 |+ @! L' u! K
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)* K6 A5 N1 p- z3 r# m" W1 |
{$ z! t4 v* I. X) h9 w* w) C
8 ]$ [; a- O2 R5 c /* SD卡的启动过程 */
2 R6 w/ n2 C) q: y! g2 P: T# \ B* P4 b
mmc_go_idle(host);
. ^3 K+ P* H! }3 R# W! w8 v. K# ]0 a. u8 b' Z2 P
mmc_send_if_cond(host, ocr);
$ l; y" W" _8 h# X. {1 G( r2 R0 {" O0 h( M
mmc_send_app_op_cond(host, ocr, NULL);
8 x" H8 ~( G1 L$ }( @9 D6 A4 L
6 Z: y7 c y& C" T6 D. p: A mmc_all_send_cid(host, cid);; f& L8 a% i2 V- S! |! E7 A
& i# @- p( a) J: j6 ~: L
mmc_send_relative_addr(host, &card->rca);
$ l7 a0 ]2 \3 N6 w. T, }# Y; ?- z/ \, `, h& R! J1 n( p
/* 得到寄存器CID, CSD, SCR的数据 */
- c" @" A& A; j* A1 R5 @4 G; z& O7 u8 f5 D
mmc_send_csd(card, card->raw_csd);$ E( N! G8 M! \, Q8 J6 R
# y" J) D- V+ W' ^8 y9 `
mmc_decode_csd(card);
4 f$ E2 v8 H4 F/ X- K8 Y- ]
: z C7 {/ l& P: ~% r mmc_decode_cid(card);
: v2 p, `9 c- _. H" g# O2 U% m8 k. J: c$ [- E: {% o
mmc_app_send_scr(card, card->raw_scr);1 Z- U7 c; c' D& N
3 l( j( |+ {( |5 B4 g# I% p
mmc_decode_scr(card);* r1 M4 _8 A3 I
|4 X8 x# k" [3 ]: d c
/* 其它操作 */4 _, W) A! q0 ]$ ]6 e4 F
6 {: e* x0 n- r, G( R
mmc_alloc_card(host, &sd_type);
* Z% C5 a' i5 q* t
8 J' Q) ~. o$ R% w! {8 s. Y mmc_select_card(card); + ~- K" n5 j, R% [8 F# o$ G
% A9 N0 r8 l+ t ^* I5 G; \/ M
mmc_read_switch(card);% {( G; d; m1 d# e) I" a$ O' K
/ J8 h7 L7 X7 d; u
mmc_switch_hs(card);
8 j9 ^0 `$ e, K! | r W* R3 r+ }+ R3 T) J. [- F
... ...
: Q0 Q: s8 W: I; D# U: I2 P
3 _0 u# j- N& a0 q4 L) l}& i6 e/ X% R1 u# l
! A6 U' {) ]$ t2 I8 _+ _. i1) SD卡的启动过程
9 z2 z: `4 H) q- J. w: }: {$ y) G7 c3 }
根据SD2.0协议,SD卡的状态可分为两种模式:卡识别模式(card-identification mode)和数据传输模式(data-transfer mode)。这里,我们关注启动SD卡的卡识别模式。
% o/ v% i# m- s% q9 `6 g
5 P& z. h0 T/ E6 H# P; F
q" d3 E9 o! ]0 @1 i8 b
( F1 J/ Y% C$ L8 X4 M" j' z4 x结合代码,; f, e/ M+ [) M7 V/ e
& Z( D+ `, }7 s0 p( o8 Y mmc_go_idle(host); CMD0
! H* `: r( b6 B' f) S P( |
1 A& l# [9 D+ Y8 _) D7 y: ^ Idle State/ w2 w1 ?- v5 b# ]6 Q% g
& M/ v) p, q: h# m$ D1 q# q5 N mmc_send_if_cond(host, ocr); CMD8. Z% D* B$ U- F a. j
$ @' j! u7 o. [: `
mmc_send_app_op_cond(host, ocr, NULL); ACMD41
' ^$ J5 O, ?! t. l# p/ f6 F& B0 |. }( [
Ready State
8 ]# K1 a I+ K+ w" q( R
) m. }2 k' B% T9 e: \. u6 y( t mmc_all_send_cid(host, cid); CMD25 x( L/ J. z5 o+ c& A, q/ S$ {" p; [
& J" W) c+ W" Z% b- n IdentificationState4 J9 O, j' Q& O/ ?1 U
) H2 k& F$ `6 s, H9 N/ ~3 f: S mmc_send_relative_addr(host, &card->rca); CMD3
1 h X6 r" H, Q- n2 ^" W- n d Q4 q( q
7 S, T( g- v; W Stand-byState8 z+ l- W" S3 T n' q* z4 ^8 v
/ `: W K" I4 u+ |2 A2) 寄存器CID, CSD, SCR, RCA
$ L/ p( I8 }' I9 u! U/ h) d( ^
% u1 a3 k( E- K" g0 x2 m ^-> 发送指令并得到寄存器的值# N/ E; Z3 T: y, |- O" V0 R
& P5 v( I) v1 Q- h6 N3 Q 当主控制器向SD卡发送cmd指令,比如mmc_send_cid(card, card->raw_cid),请求得到SD卡CID寄存器的值,当主控制器发送cmd完成后,芯片产生一个内部中断,处理结束 cmd的中断函数,之后得到来自SD卡的response,即CID寄存器的值,存放于host->cmd->resp中。关于内部中 断处理,参看上文的中断一节里的 mmc_wait_for_cmd()。" S, i% X. E% |" U
5 @0 P7 s9 Z' B( y# h1 ^. n
mmc_send_cid(card, card->raw_cid);这个函数发送了接收CSD寄存器的请求,并且得到了来自SD卡的CSD寄存器的值。! v: u x# ?4 I/ Z0 V+ W
& n3 H$ T. d! M5 jint mmc_send_cid(struct mmc_card *card, u32 *cid)
9 C2 m) C" j4 I, {/ V. y0 y{
$ I7 O; w( }1 }) v: N k return mmc_send_cxd_native(card->host, card->rca << 16, cid, MMC_SEND_CID);: U7 J- i! r$ H7 Z- x
& c' Z2 ~* k! {: B& y- J! z}
0 E9 M7 [5 k% y9 I7 f: h& L( i. b1 c: n1 Q5 I
static int mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, intopcode)
) I9 x3 ^$ `) x8 W{
* k9 @3 U9 z8 K9 y cmd.opcode = opcode;2 j4 a# J1 Y. |/ q( G
cmd.arg = arg;& M- w3 }/ }3 j
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;# s. B' D4 W* X% j* v
! }8 _; c6 n8 i5 X0 c7 n mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
, Y g# k# m" C1 N1 ^% F
3 \* o/ j5 C* P. O% v mEMCpy(cxd, cmd.resp, sizeof(u32) * 4); // 得到response赋给cxd,即card->raw_cid( |& y4 e: F) q) Y" ~5 L
* h) c. J9 f! T5 }, z8 n ... ...1 Y6 Z R0 M3 F+ ^/ l9 c- v7 m
}
4 v9 _! M) h0 A# d! u7 h: _/ z6 n0 s( v! y; G" v& ^8 M
-> 解析寄存器的值" V0 x& [- y7 R6 Z
% K# [. `* I9 ^0 n4 [& Q
为什么要解析?先来看看寄存器CID在SD卡协议里的定义,它是一个128位的寄存器,存放了关 于这块SD卡的基本信息,就像自己的身份证。通过mmc_send_cid()将这个寄存器的数值赋给了card->raw_cid (定义 u32 raw_cid[4];) ,为了方便得到具体某一个信息,协议层为我们解析了寄存器里的域,并赋给card->cid,比如厂商名称,就 可以通过card->cid.manfid直接读取到。8 W$ p7 P9 n( }+ h
+ I9 \. n* {+ L
% a. }. }2 F7 ~4 m9 t
+ Z2 P0 c5 a8 F$ Z; i; Sstatic int mmc_decode_cid(struct mmc_card *card)
: Y- e6 y, V- n$ V6 O( N( b# Q) ?! \{
- e0 K b& h) U" E u32 *resp = card->raw_cid;
. Q F; b( v* e, N: I0 z& ], Z
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);( d% X& H+ I7 z$ Q. a8 @' y' @
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
7 X0 f. N g$ \- ]) i) r5 p card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
) B6 e2 G0 }* u9 [) ]2 N& J4 _ card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
( Y+ v, L0 v9 [0 R: V card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
" m* F* a7 P* N; m card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); J" U% ^; e! s+ S7 a
card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); k# c$ Q+ P7 I! |- q
card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);; ?3 F3 m2 D3 s" U' h6 d" {1 {
card->cid.serial = UNSTUFF_BITS(resp, 16, 32);5 A Y2 t# r0 _& j
card->cid.month = UNSTUFF_BITS(resp, 12, 4);4 L6 E, h- ^7 _* G, J
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
0 c2 [0 [8 P2 N- r return 0;6 _# [+ ^- t0 a. R1 I6 n) W
}
C+ f2 n, T3 Q! ~& U3 X9 X- I; h5 q" D+ s
3.1.4 注册SD卡设备驱动
' b* Z; M5 b* r& f+ l8 G. @
0 p% s% P9 c9 s+ a' m int mmc_attach_sd(struct mmc_host *host, u32 ocr)
4 t/ x- F0 N0 O, E% g1 i" I* A{
; p# U3 E4 p+ }* w1 j& a% W' [- Q( e7 |9 T0 X" i' D+ h
/* mmc_alloc_card(host, &sd_type); 在mmc_sd_init_card()已完成 */0 j1 q) I4 z3 n W, T3 U/ p
+ K+ F1 T. F$ f' `
mmc_add_card(host->card);
9 z5 Z* T5 h' `2 z9 |
& ?' I7 p) m6 V L' _9 O) ` ... ...
% @2 o) |& y8 z* ]5 j4 L9 c; G5 p+ @ w! S
}
/ i" r1 h- B. v1 r6 f: Q( ^
& M& m: [0 \. x) ^( }0 O上文已经提到,设备驱动程序都会通过alloc_xxx()和add_xxx()两步来注册驱 动,其实质是调用/drivers/base/core.c里的device_initialize()和 device_add(),device_add()完成建立kobject,sys文件,发送uevent,等工作。6 F V a) l' Y3 C& m9 p: S. D5 _
) |8 @2 Y9 y H, h; n6 r4 H3.2 拔出SD卡
- ^$ d2 J( }* g6 B& v
# ?) M3 {5 f) {- T! j void mmc_rescan(struct work_struct *work)
" {: B" Q5 D; r$ l K6 @1 n& e{
# I8 T$ L) m5 A5 d# w struct mmc_host *host = container_of(work, struct mmc_host, detect.work);4 @" p$ s! |$ K' k l
mmc_bus_get(host);2 C1 u* z( @" ?6 ~6 w
9 q8 M( ]* O3 H+ c
/* if there is a card registered, check whether it is still present */
% m: L, D6 d9 O' x9 I+ d if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)2 Y6 M; M; m0 Q- g! T9 z/ i+ S
host->bus_ops->detect(host);
; b( b4 t h7 j9 _$ K) P
+ T0 o. B% ~( E& O5 B mmc_bus_put(host);% D; N/ l, ^" p* }' Z# q B _
- A. H d; [& g( V2 d: l6 l3 A
... ...
# M. K2 |2 e2 {& ~0 I* ?: Y* ~- a/ I) s" D: X6 {
}, Y, i0 A- a' a' u
" u( a5 a$ L# v$ y0 R2 r这里的mmc_bus_get/put(),为SD总线加上一个自旋锁,规定同时只能有一个线程在SD总线上操作。2 i# B8 Z a& }0 X+ L7 @
9 p7 d% m3 |* H7 I' y/ M9 ~' Z3.2.1 bus_ops->detect()
7 Y6 h, Q; W' l5 ^4 K
* e/ b: F8 L# X- w( Y5 C# V% d7 Smmc_rescan()扫描SD总线,如果发现host->ops上赋了值,即之前已有 SD卡注册过,就执行bus_ops->detect()操作去探测SD总线上是否还存在SD卡,如果不存在了,就执行 bus_ops->remove()拔出SD卡。之前已经提到,这个bus_ops->detect()已在mmc_attach_sd() 注册完成了。5 h$ W2 ^; m$ V, _# T
$ D/ x! Q! J7 C4 m* _ static void mmc_sd_detect(struct mmc_host *host)2 c2 c" s! d( o
{
- P5 S3 l" V! \ mmc_claim_host(host);3 C/ Z: f- I$ F. x
3 a+ y# M; j2 `* G* u
/*3 ]" ] ^) ?0 p, C% J! J8 {
* Just check if our card has been removed.
+ ^9 H) h& `" n */& z- O( c S; ]; t! ]9 h% @% `
err = mmc_send_status(host->card, NULL);7 W$ c7 g7 l1 A# i- K3 H
( \1 j+ X7 O& x0 C! T8 n4 |, E" v# N
mmc_release_host(host);
/ z+ l, r" i/ e ~) c# ]* o
2 g# i- z: ?6 |6 y, g, E/ q if (err) {. D# f7 [" T/ N% W6 M, b" [
mmc_sd_remove(host);8 s# w# U6 ]! N0 Z; B3 n$ K
1 t. S3 [ l y9 ^. [1 ?# P6 }$ o mmc_claim_host(host);# U2 I4 f/ Z) E6 {0 z/ f
mmc_detach_bus(host);
: i4 M% b/ K2 K/ s6 ` mmc_release_host(host);
5 S; d8 _6 ]3 j F9 k' k( [ }+ D( H6 v8 S$ b# |
}" G6 _3 y! S! f O# h) p
这里的mmc_claim_host(host)通过set_current_state(TASK_RUNNING);将当前进程设置为正在运行进程。
2 B! l, V7 e9 R; r9 J" X' A5 c+ S4 F1 x$ M% V5 s% x% b$ j) }6 W
mmc_send_status()发送得到SD卡状态的请求,如果未能得到状态数据,则执行mmc_sd_remove(host)拔出SD卡。. U) ^0 O. b* H9 Q4 |
4 r( n1 a1 S W r9 Fint mmc_send_status(struct mmc_card *card, u32 *status)
0 W/ M2 G! N* x$ \- X# w{
( _ L; W5 P& ]. S# Q; S struct mmc_command cmd;' T/ k' i- C e: \* o
" \. a) ~; f6 p cmd.opcode = MMC_SEND_STATUS; /* #define MMC_SEND_STATUS 13 */
2 U i* o5 A3 w4 ~8 v cmd.arg = card->rca << 16;, T" S8 I' Z, r6 u- e/ W6 v
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
9 Q: z4 T4 u6 k5 B. J$ y
' L% Y6 c# l0 B+ E err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);* L V& C' c+ E% ]2 N/ ?* g* {
0 h# j. t+ S0 ?, j g
if (err)
9 a+ ?" j& u$ m- a return err; // 接收来自SD卡的response失败,即没有发现SD卡. |1 {* q' j! `5 H8 f- f
if (status)
( ]; w# p6 Q, L7 i *status = cmd.resp[0];
+ Q% y& P( b# c0 @$ I
4 e! ~: j/ H: f4 N# |( C return 0;. h4 y6 D8 Z5 |1 a9 t
, Z( i+ c$ D: y; z7 n: m
}! R/ }( q! A' ~5 j- B. c s% W- G$ y
主控制器发送命令CMD13,要求得到SD卡的状态寄存器CSR和SSR。# w$ Z/ I) c3 U$ R
: L- P; w) k$ f8 Y
SD协议规定,状态寄存器CSR是必须的,这个32位寄存器作为R1的一个域返回给主控制器,
& R0 ?* Y @5 g: P" c. ]$ V& g1 `- `. c* w' P3 M7 k
状态寄存器SSR作为扩充功能,具体参考SD2.0协议。6 ]( x& d& {$ U o B2 K
5 q9 w3 v$ D( e" x' S; l$ y' V) z3 r7 e2 @* i: f- R: n
3.2.2 bus_ops->remove()( _9 q7 G/ j5 m/ z
. J! G2 d y; m: S拔出SD卡,其实就是注册SD卡驱动的反操作,实质就是执行device_del()和device_put()。4 h- W; V6 T( L
3 N# I1 n8 ^. E5 ?1 X3 ?: Kstatic void mmc_sd_remove(struct mmc_host *host)
& _. Q+ K8 f( }* o& D- ?/ R ?2 `+ d{0 ]6 T% u3 L, d
mmc_remove_card(host->card);9 [) u( \4 l) n
host->card = NULL;
) V6 X- \! p- }" h# b4 l6 f}
' U* |. x1 R! h4 U4 ~4 mvoid mmc_remove_card(struct mmc_card *card)
1 K$ ~/ Y/ q; @{
) w: z' U; q% B/ X i) t5 X( A if (mmc_card_present(card))5 c2 Z2 o6 k( `, @+ b4 t4 B8 [
device_del(&card->dev);
7 l0 A4 G8 B0 T& N$ T: [
6 x" R& D# T: s- H put_device(&card->dev);: f3 W- k O( o# H- k
}' K+ C& d% B M8 H' X( ^1 T4 Y8 i
) p% `. B% m }( r4. 块设备
+ V$ Z: }; D' I% [
" R2 W/ w* a4 x: u- h8 L首先,必须知道为什么要用到块设备。在linux下,SD卡通过block块的方式(以512字节为最小单位)进行数据传输,它必须遵从块设备架构。在linux块设备层,I/O调度者通过请求队列机制负责对块数据的处理。* l$ T, V F& v6 V
; k: e6 r6 J0 e& k0 ?% {0 N# S# |' K7 H
1 d; L1 G7 o" w! a4 i
SD卡子系统分为三层,主设备层,协议层和块设备层。块设备驱动位于/drivers/mmc/card/block.c,主要完成两个任务," o/ `( \1 G- J; ]3 h: a
1 c( C$ C) s# M2 z2 |
- 建立设备节点
- 通过请求队列机制进行数据传输8 d v% x0 b( @' a& {
# k# s3 D- T8 @" `/ U
+ @/ k8 r8 ]) L" k
插入SD卡,注册驱动成功,那么在开发板的目录/dev/block下会出现SD卡的设备节点。
# n/ d$ F7 b! D6 B. W* o
% @# g2 Z- R, O9 }: U' C179为主设备号,定义于include/linux/major.h #define MMC_BLOCK_MAJOR 179
3 h/ O2 h2 L- ]$ r; j% o1 D2 }! @" o/ H- X
179:0代表这块SD卡的设备节点mmcblk0,179:1代表这块SD卡的第一个分区 mmcblk0p1,即主分区,如果有第二个分区,那就是179:2,最多可以有7个分区,即179:1~179:7(定义于block.c alloc_disk(1 << 3);)。不过,SD卡一般只有一个分区。如果有第二块SD卡插入,将会建立设备节点mmcblk1(179:8)和 mmcblk1p1(179:9)。 : H9 m; U9 G* ?% @4 c% P) W
- @. @2 G& @* ]9 H/ h8 y: y. _) U下面通过对块设备驱动block.c的分析,看看SD卡是如何在块设备层建立节点和传输数据的。
7 a9 K& J: m/ Y- s, `
1 V- ^, z8 D5 J; _2 i% @4.1 数据结构
7 L" G: a0 n8 T+ a$ p# T) ~' a# p; [6 c" n l5 q
每个驱动都会有一个数据结构。幸运的是,我们SD卡块设备驱动的数据结构相对简单,在mmc_blk_data里,主要有两个成员,struct gendisk *disk和struct mmc_queue queue。
! ~1 K& Y1 S0 q/ @9 r
, j2 D$ o( o0 d2 z5 f" R1) struct gendisk 是general disk的缩写,代表个通用的块设备,其中包括块设备的主分区结构struct hd_struct part0, 块设备的行为函数struct block_device_operations *fops,以及请求队列struct request_queue *queue等。7 W; r; R: F; z' R
7 J: B3 ]: V8 D, T- g. ]& ~ Y
2) struct request_queue 存放所有I/O调度的算法。& B) b: |: N$ |2 j
" U6 I, N2 L# x8 y3 C/ Q3) struct request 请求是I/O调度者调度的对象,其中的结构struct bio是整个请求队列的核心,具体内容请参看LDD3。
, ] I! M0 C$ [8 e. U5 A+ D4 ^ Y3 J9 g
/ z6 Y o2 e7 Z4.2 块设备驱动
7 `- N7 G p1 H2 Y$ l# Y( f) i6 C2 T
首先浏览一下源码,
! L' m, X j$ B8 C& X* }
! A4 X3 B5 j" r1 l- Q9 Q/ }static int __init mmc_blk_init(void)
" q0 P: @4 Z5 T% Y' e6 |{* z" ]* w h" _
register_blkdev(MMC_BLOCK_MAJOR, "mmc"); // 注册主设备号(若注册成功,/proc/devices的块设备下会出现mmc)6 @$ F" m1 T4 h: v5 C
mmc_register_driver(&mmc_driver);
8 [: A9 X3 [9 u) d2 x return 0;! r; j) ~4 ~! J4 X. T3 K% A
}6 a2 m" l8 v$ j
! p6 U: q( u b# C
static struct mmc_driver mmc_driver = {& H. m/ T/ o& k( q
.drv = {1 o6 q( {: J3 d# P% R/ f
.name = "mmcblk",
8 T7 V' A/ e' [% i4 L },/ O3 m9 T8 P* j4 M, N
.probe = mmc_blk_probe,
& s; r% }1 H3 [$ R( n .remove = mmc_blk_remove,
; U' H/ ?' P/ O) U0 | .suspend = mmc_blk_suspend,& k! V# p* @ M" q+ S% @2 i) V
.resume = mmc_blk_resume,: H- V2 H$ V4 O. ]* d# e
};' y8 e# X3 d4 A. M0 ~2 ~
$ c# g9 N/ Z5 `static int mmc_blk_probe(struct mmc_card *card)- H- x4 o+ y6 _* T8 m2 `) Z1 B, t# c
{/ ?3 @9 I$ V# L5 J4 B/ J5 U; o) Z
struct mmc_blk_data *md;
% T# v1 B6 a+ L8 a/ A: } md = mmc_blk_alloc(card);
2 a# @' h+ {. G6 s mmc_blk_set_blksize(md, card);0 r& ], t2 w2 h. a2 ~" o8 T, s
; X T3 p& @% T- {& o
mmc_set_drvdata(card, md);
6 N+ M' T. Z0 X4 X% A# d Q add_disk(md->disk);: |* @5 W8 f) [7 e @# f
return 0;2 f) a8 c; X( [& }
$ f1 j- h2 C! y- K ... ...& Y# H2 o; ^9 I6 P" B( r
7 D6 G: X: \3 [3 A" N}
* o1 `+ Y+ F n* |" o9 f$ v( }; v* h1 u& V8 C# F
4.2.1 设备驱动的初始化函数% G. [1 p0 `" k7 g( o7 k5 G
& w- R3 E2 s+ I; m. L% e% d 仍然可以将驱动程序的初始化mmc_blk_probe(struct mmc_card *card)归纳为以下内容,
" M# I% Q; z/ _- J& o( V6 C% M C
( M) v( Q; T7 E% k3 f; L' t初始化设备驱动的数据结构mmc_blk_data,并挂载到card->dev.driver_data
# \6 V+ W- A# a" D( O+ y实现块备驱动的功能函数struct block_device_operations *fops& Y" @: r& O( K l: n0 b
注册设备,即注册kobject,建立sys文件,发送uevent等4 h: w& o: ] V" n3 ^
其他需求,如mmc_blk_set_blksize(md, card);
* N; L2 h7 R/ R& ?1) 初始化mmc_blk_data, | ~9 J+ ^/ J- T
; ]: T, c f8 Qstatic struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) e6 e# T+ z: {' ^
{! z3 X i& X! T# r# V* o# ~# a
struct mmc_blk_data *md;
0 s* ^ ]& h T9 c" [ md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); a9 c1 Q: p, T
d( O# X7 X. q5 t+ b
md->read_only = mmc_blk_readonly(card);" s* g: _) R3 P) g
9 r, r A8 c, P% H: Q s/ C% f
md->disk = alloc_disk(1 << 3); // 分配了8个可用设备
% [$ d; R8 `0 F
2 i# \$ ^5 p! {& `0 H3 ~4 G spin_lock_init(&md->lock);) G6 R, G' o; L, {, r Y
md->usage = 1;( I2 R( g' x$ B; Z0 E
ret = mmc_init_queue(&md->queue, card, &md->lock);" Z) u5 {1 r Z3 n3 I6 X
$ Y. q. h. q; f7 [ md->queue.issue_fn = mmc_blk_issue_rq;! t# u3 x# a( A, i8 Y- [
md->queue.data = md;) x8 C. L3 u' G! R+ a: @7 B% H
8 V) R3 s) C U0 t$ L5 c: Y' N: I
md->disk->major = MMC_BLOCK_MAJOR;& h# @' b* C4 A, _- U* z" J
md->disk->first_minor = devidx << MMC_SHIFT;
$ u5 J# K- j5 O, |! h md->disk->fops = &mmc_bdops;: E, o( w2 r8 i
md->disk->private_data = md;
( s# y R* @7 Y6 \/ z! Q6 w5 A md->disk->queue = md->queue.queue;( U/ }5 T! N$ F2 G# Q% V1 w
md->disk->driveRFs_dev = &card->dev;
7 ?, o) x S) T- }2 c, W' E- [: f4 P9 N) y" H/ x( T
blk_queue_logical_block_size(md->queue.queue, 512);( T; [) T7 D4 H7 H Y
( ^. @3 B" `, v/ B. O
... ...4 q: `% d$ h2 I7 s& m" N5 J/ L
x7 Q! f( o o6 a, }
return md;; a9 D- r B1 p" b6 ?
3 ]) x8 ]: {# x1 i, F- ~: K6 F; }}# @% Y2 P: J- k' W# V a
: W0 T; b! R( Z# [9 ^7 \% f
完成初始化后,通过mmc_set_drvdata(card, md);将数据挂载到card->dev.driver_data下。
8 V) l! X6 B$ [1 R+ f; g- B# `& v/ v4 S* l: Q8 e
2) 功能函数0 U: U# q8 g$ z" [ G0 S
# @ F/ r- h# W" j$ |
static const struct block_device_operations mmc_bdops = {
: h) |. R2 b2 ]9 c1 y .open = mmc_blk_open,
. P/ E) ? E& h. R6 T# N .release = mmc_blk_release,
" D) L, F. c6 r9 ?+ y! d .getgeo = mmc_blk_getgeo,' f, s4 D7 N8 v* v3 t& a
.owner = THIS_MODULE," c% p# |& l4 D( A0 I: S
};( A4 L7 m" _$ Q
. w! N; c( o8 T% t0 _4 q' |* z
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)" _" D4 @" O; m( t' ?) {
{
$ Q) [: V- [! c" e5 h: N. C struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
1 @) v$ Z' a3 ^6 b4 J* \0 Z' r5 _. Y/ }3 j) }: w
... ...# q$ k! P# }1 m. k. H9 E
}, w5 V6 U) a, f9 d q- X4 b7 ~* ]
struct block_device {
( t& W9 i+ @ F/ G: ~. I) S% s dev_t bd_dev; /* it's a search key */7 i1 o3 F2 h# F5 H
struct inode * bd_inode; /* will die */
5 }( J0 Z1 Q* i- }, D3 y. l1 s struct super_block * bd_super;
& H9 i5 g8 j: q% `" {/ S o* F
@- |2 N4 w; p; z# J ... ...
, R5 V7 X. u# H# ^ W};
2 ?( G+ d) E% W& ^! L; v; b' F* [
$ p1 ]! s6 N+ f! A与字符驱动类似,通过dev_t和inode找到设备。
% a3 P3 `- \& A( t- C, U1 b Z
$ r5 E2 k; A9 B1 \0 m, f0 M3) 注册驱动
8 O- Q+ X) X4 A" v% t
) h, g1 V) [/ F* {! ]$ Yvoid add_disk(struct gendisk *disk)0 q* V. b) h; l0 O) Q
{9 k2 D2 a( ?6 R1 T! P3 @ r" U
blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk);
+ W% {) s4 F7 ~$ w4 q5 f& Q register_disk(disk);
T4 r% R8 `. R& X8 w0 t j blk_register_queue(disk);; ^$ N7 C0 d& M9 a4 ^( ?, |
! ~& z# F" f/ ?+ p4 f+ D& a3 V* ]- k ... ...: z' \, u( h# l# G
' x, P. S& |" B6 }6 x6 u5 d
}$ U" {. X$ [7 D$ O" }4 Y F3 A! Q
" K( `4 P5 a4 b# Y0 U7 M5 d/ ~
blk_register_region()在linux中实现了一种利用哈希表管理设备号的机制。
# J2 _& _2 C% T
5 F/ F1 m5 u7 M k1 Oregister_disk()对应alloc_disk(),完成对块设备的注册,其实质是通过register_disk()->blkdev_get()->__blkdev_get()->rescan_partitions()->add_partitions()添加分区,建立设备节点。/ |( W( n/ o: ]& b e) \* A6 M
7 a. r( S9 Y! T
blk_register_queue()对应blk_init_queue()完成对请求队列的注册,其实质是通过elv_register_queue()注册请求队列的算法。2 f0 F/ c! x- F
5 @$ r* u% c& F! J
关于块设备更为具体的代码分析可参看linux那些事。
+ s. Z$ ^) Y6 m* ?0 f) V* _4 K3 v9 U
4.2.2 请求队列
0 h" t9 C; w. [1 Q
1 [- f3 i" W# ~4 N4 T3 ]mmc_init_queue申请并初始化一个请求队列,开启负责处理这个请求队列的守护进程。
1 U. Z/ y. [+ c' l/ K: O$ V& `$ B9 a. c8 c
int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock)0 q; d; W4 f. U( f5 F. j, S
{
4 z+ G5 V. p1 j. S2 T struct mmc_host *host = card->host;# [& w) n9 C* o% W& B
mq->card = card;
, D M3 g" d" @/ S/ ^ S3 N& d; u0 a mq->queue = blk_init_queue(mmc_request, lock);* c! F R4 h) A& ^3 l4 z5 j
- w, L- k. Q8 P% Z mq->queue->queuedata = mq;
5 \5 \. P! E' A) b8 s mq->req = NULL;2 M4 k, Z% l5 d: P' `) t- W9 w( e
2 y, M3 t! ^7 E; m* q6 n8 [ blk_queue_prep_rq(mq->queue, mmc_prep_request); // 注册mmc_prep_request算法
: a& w. a; R8 d3 ^& J blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL); //注册ordered算法9 `/ Y Y: _* Q6 A
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");
9 U8 E" j: L; R9 }/ V1 G: E: R( i- l6 ~
... ...
' i5 X; ^+ c' v! b} f+ [5 q7 \# j) q. w v" S
2 c; E6 G: X& g" p4 W3 r
1) mmc_request# }% `% \" }% j2 M7 k0 s
7 v) E4 Y; S& c2 p& e它是处理SD卡通用的申请请求的回调函数,或者说是SD卡申请请求的算法。当CPU处于not busy状态,会寻找一个请求,并试图执行它。: H5 }! v* F8 N, D6 ~; m- u* G+ f
6 {& u% o# q I) c
/* /drivers/mmc/card/queue.c */$ C. F# y N: B: ^5 [# h9 V& E$ h
& Z$ ^) `1 U) p. B
/*
E" }5 o9 h: ]$ ^# p * Generic MMC request handler. This is called for any queue on a2 M+ m9 O5 t J6 K6 c
* particular host. When the host is not busy, we look for a request
- z& O+ ?% w7 W5 V * on any queue on this host, and attempt to issue it. This may
. ~+ V& P5 M) J- w8 Y * not be the queue we were asked to process.
9 q: v1 G/ W3 T4 p/ } */" Q) B0 V+ i' r" d! }" }
static void mmc_request(struct request_queue *q)4 Q+ B7 W+ a9 T+ A
{
& c" b! m2 l5 r3 M+ l struct mmc_queue *mq = q->queuedata;
7 E' N% p z2 ^# _& U$ i+ O5 r struct request *req;
Z6 Q0 ]; o$ E* S6 a' X% N' A4 O I
: G2 g: A3 a) ?, g* u if (!mq) {
1 v8 x/ i, N$ _5 R& F6 g2 E% M while ((req = blk_fetch_request(q)) != NULL) { // 寻找来自请求队列的一个请求req
( G' s) P) J/ y4 e) ~( @ req->cmd_flags |= REQ_QUIET;& V/ j2 V( E& ~; n+ B V) ]
__blk_end_request_all(req, -EIO);
' L& H: e' F$ v' i }
+ J: {5 K0 t! \- l q! } return;9 ~' ]2 i @+ g9 b% C: v+ k4 x; s
}% m& {" ^1 b0 |1 _5 a. C$ m
5 G9 s s/ w& A
if (!mq->req)6 t3 u$ W3 y P2 Q3 T+ I
wake_up_process(mq->thread); // 如果队列里没有请求req,唤醒守护进程+ u* Q3 D% M; X7 K. C0 f) {
}8 U9 C7 _5 H }
) v+ b* I# k6 F$ j
这里我们需要关注这个处理该SD卡请求队列的算法是何时申请的,也就是何时会去申请请求,何时会去唤醒内核线程。
$ S" F- J; H' z+ L' A" L) }2 U9 n$ M# g* R H0 x2 u* G
用到回调函数q->request_fn有三处
6 J8 q: F) k8 n L/ R1 T9 h
( N) g& L, h! E- y+ y2 L- 块设备驱动注册请求队列blk_register_queue()
- 驱动程序出错,清空请求队列mmc_cleanup_queue()
- 实现请求队列机制的blk_fetch_request内部本身
7 u2 H' M4 |) U5 J1 C; p$ [
3 {" s8 [) I: D7 T
' I6 N( c: g3 u5 lblk_fetch_request()->blk_peek_request()->__elv_next_request()->blk_do_ordered()->...->q->request_fn$ P% E, t/ f6 ~6 ?- g! n
& Z' H) o2 V- C0 ` M我们不必深究所谓的电梯算法,只要知道,它是使数据得以高效通信的一种算法,算法自身决定何时去唤醒守护进程处理请求。
) j0 n/ u3 m3 _) g: b
. w9 V% ?, m5 ~1 j2) blk_init_queue()( ]) m) h. K( U1 y
$ k5 H6 v% M! W# l
如果一个块设备希望使用一个标准的请求处理步骤,那就必须使用blk_init_queue()。这个函数注册了q->request_fn(这里就是mmc_request),并初始化请求队列的数据结构struct request_queue。
. W+ s! a& Y3 M+ V5 Z9 h- M
9 U2 c+ R G6 e: I/ ^/*
; Z3 v2 z, }1 g4 ], ~( y, v V) O# l- k ?5 N2 b1 q1 l
* call blk_init_queue(). The function @rfn will be called when there
7 ` l w/ l. g6 T) i0 ^ * are requests on the queue that need to be processed. If the device
, n! o0 X k: x. P; D0 s * supports plugging, then @rfn may not be called immediately when requests
, X' ^" p$ \5 [, Y# U" I) M+ O * are available on the queue, but may be called at some time later instead.
% [3 V2 v2 W7 X) m4 V; b; f! G */
- m' Y! H* j% C6 ?- K$ R4 V
1 R! y; z* C/ @4 U; w9 F struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
5 I h2 F5 v3 `5 v. S' t @7 \% _! V{$ F' {- v. B/ b" @" e9 X( R
return blk_init_queue_node(rfn, lock, -1);2 ^5 m" y( v+ t! v5 n4 d6 k1 Z# M
}- t' V) }! G- M3 ]
) T& e& s( k: n% A$ c; S5 t0 o
其中的rfn就是请求队列的一个算法,即这里的mmc_request。, j$ c7 I4 i: B9 c/ {
% a% P3 {) {/ g$ ]2 s
struct request_queue *blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
- q# U+ d5 m1 ^# C{
/ X% K3 B- }+ v: d& H& r5 X& J struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);
3 A: R+ H/ z. n, b) x' B9 a; v3 R7 O0 H, L! G# c, ] {) t2 Y
q->request_fn = rfn;8 J8 N2 d+ l, r: E1 C( v
q->prep_rq_fn = NULL;
% A+ V# ?0 R/ Q( O4 | q->unplug_fn = generic_unplug_device;" H D1 C3 M% l3 W# o- d5 Z, t+ x
q->queue_flags = QUEUE_FLAG_DEFAULT;
& \6 L5 J* y; t% V; M3 Q6 r q->queue_lock = lock;$ w7 A5 o, S* `; R9 s# b! ?
4 ~: T9 u- H3 @! \) p blk_queue_make_request(q, __make_request);
1 X) _5 T( }" a7 z
, p, u1 s9 V8 d W! b if (!elevator_init(q, NULL)) {
2 I9 U- K/ x8 {2 Q2 I6 J5 _$ e blk_queue_congestion_threshold(q);
0 f h. y, l0 l: P8 `3 { return q;
( p9 r0 C' P0 C: s }8 M$ r# G2 c' L6 [5 A6 a7 V" V
) R3 s( n# d; `; z) a ... ...
$ T& J" R+ z7 k* |5 } {( j" @
4 v' p8 V+ j4 | ]0 I ^3 E. I5 ]}6 F: b/ z j$ u+ l. ~9 n
) z" @! B7 ?1 \$ r" D
3) kthead_run()
7 Y% L" Q9 z) u/ A8 E1 e2 y6 Y) D5 h4 O. i
注意到mmc_init_queue这个函数的最后,创建并运行一个名为mmcqd的线程,顾名 思意,mmc queue deamon它是一个SD卡的处理请求队列的守护进程,或者说内核线程,当系统注册SD卡块设备驱动时,就通过mmc_init_queue()开启了这 个内核线程。
1 h+ K! q& p. M
/ V* [% W) C. J5 P. j4) mmc_queue_thread 1 D6 e* I8 p( \
5 M' ?% x! D: U" [! y% \* X, }看看这个内核线程做了些什么,
/ f7 X6 a' ^" ], U4 E+ I6 ?( B9 U; P: a x4 Q
static int mmc_queue_thread(void *d)1 l9 f) F. Y' ^8 y/ ?4 M% V
{5 ^5 L* A) R, M" r) e! U2 d
struct mmc_queue *mq = d;
, W4 }2 F2 d; x6 T: V! p struct request_queue *q = mq->queue;' R+ ^/ T1 u4 @/ n
, j/ `! m+ q# l' n7 w- z5 n# j current->flags |= PF_MEMALLOC;! h$ R v# T$ [5 [0 P p
+ P Y$ K7 E H! X9 _
down(&mq->thread_sem);! n8 n' g6 L2 }: N
$ m' P: G4 _* ~ |& S6 W
5 {* I1 j+ c8 j" ]' S( ~
do {! @( T) {0 k+ G f5 P
struct request *req = NULL;7 b5 k. n* J$ M: _4 Z
! @$ {" S0 A- U* ?3 i& z. } e spin_lock_irq(q->queue_lock);
; s' w& O6 M9 D9 v+ V3 T set_current_state(TASK_INTERRUPTIBLE);
+ V( z, C2 {+ L8 B; B: q0 p8 A6 N4 l if (!blk_queue_plugged(q))
* S+ w& r0 s- T) ]3 Y" c req = blk_fetch_request(q);( a2 w4 j1 |! b7 m+ `/ Q& H, A
mq->req = req;
$ |$ r9 T; I; \2 Y6 B. ^ spin_unlock_irq(q->queue_lock);
y. P% N5 D! P0 c0 r% S" U6 e8 v: p
if (!req) {) h6 Q: q) s8 w$ T- Z+ D
6 ] G+ F7 A: X
if (kthread_should_stop()) {$ B. l+ w. {7 U( n/ g- V
set_current_state(TASK_RUNNING);3 E9 A- K4 z- }& k5 F+ w1 J
break;
+ H7 Z0 T0 ^4 I E' C6 a, G }
% s+ B) Z- b9 h9 G/ R7 Z up(&mq->thread_sem);
n9 W. U" c: f# C0 i) I; c( P schedule();
- c% f7 G2 U8 {* k3 J }$ D) ` down(&mq->thread_sem);/ ~2 A( V5 z+ h8 p9 {) l& T2 m
continue;
5 Q7 V0 `' b) J }
) q) s3 I( b+ W# o/ [ set_current_state(TASK_RUNNING);4 d7 s7 I3 O5 @
/ s K" v9 s% N! F
mq->issue_fn(mq, req);
3 w5 i: m; H* t3 d# I$ s& s } while (1);" G% @' U0 R/ ^" {! D- g; W
/ X# g& F% }0 v! Q
3 x# o7 d8 k( U* O
up(&mq->thread_sem);8 j1 W5 T3 _- C" P' ~9 M" k: d# s0 E
/ c6 `- `# p, j: U return 0;- S' d; p4 v3 e$ e% a: S
}
1 V% i; x" ~* \8 P( F& [
3 w& m0 d# k2 g6 ]首先,这个守护进程是一个while(1)死循环,如果没有特殊要求,即kthread_should_stop()指定要把这个内核线程终止掉,那么它将从系统启动开始一直负责处理SD卡的请求队列。+ U+ I1 d% r9 ]3 B
' c) A9 b7 u" j$ q/ H+ p6 \& \: I在循环内部,内核线程首先通过 set_current_state(TASK_INTERRUPTIBLE);设置当前线程为可打断的等待线程,进入睡眠状态,等待其他线程唤醒它,这 里唤醒它的就是处理SD卡请求的mmc_request,当mq->req为空,即当前没有请求正在处理,则通过 wake_up_process(mq->thread);唤醒内核线程,接着该线程尝试从请求队列里得到一个请求req,
# o8 Y1 j; f- a& g2 r( U
9 I7 g& A( e. M; p/ M Z-> 如果没有请求,则调用schedule()交出cpu的使用权让其自由调度,等到系统空闲时,再次得到cpu控制权,并且执行continue;退出当前循环,重新开始新的循环。
6 b' W* u2 y2 j2 ]% i9 \) `( ]- m) D" w: V( M, Y
-> 如果得到了一个请求,则通过set_current_state(TASK_RUNNING);将该内核线程设置为当前正在运行的进程,并调用issue_fn(),即mmc_blk_issue_rq,处理这个请求,实现主控制器与SD卡的数据传输。+ s, y/ X+ D a2 t/ e
. d! M+ r7 `7 L5 m2 b4 v1 c! e5) issue_fn
! C% b4 E* S# Z4 w3 @
5 p6 C$ F8 p, }9 o7 {9 w驱动初始化函数probe()里的mmc_blk_alloc()里注册了这个回调函数,md->queue.issue_fn =mmc_blk_issue_rq;
2 J5 t3 e3 X+ Z: P4 O4 c7 p5 Y! B* l4 N4 O& T9 [8 h5 o
这个函数将req里的成员解析成为mmc_blk_request里的指令和数据,即mmc_command和mmc_data,然后通过mmc_wait_for_req()最终实现主控制器与SD卡间的通信。" l& ^6 c/ C0 Q0 ^3 {; E- A
# Y- ?3 P }9 p: y# _; Q' L
struct mmc_blk_request {1 r& Z0 v, J( r( n
struct mmc_request mrq;
5 y) K' B6 _- b7 ^8 U$ L struct mmc_command cmd;
' I- J" I' w# w9 { struct mmc_command stop;
* \; P3 r2 p, S' Z6 t struct mmc_data data;
# S9 H* y& `7 h: G0 @& f6 x};5 s0 m, ^) I' F. s$ v8 p+ `: z
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)* Q ~+ B3 P2 Y( ~5 R
{
% v9 H2 a4 D; c2 H, y- N1 v struct mmc_blk_data *md = mq->data; O. C: p/ H P3 [
struct mmc_card *card = md->queue.card; Y* {- I7 o Z: P
struct mmc_blk_request brq;( C3 [5 a2 X2 Z; B( Q" H
$ Z$ p6 f& X! v
mmc_claim_host(card->host);
2 J2 x- V: V* u6 J# v; ]8 _- ?
; F: k% F5 N: D( G do {0 C f7 j1 H4 [! \+ U5 j6 d0 q
; i$ c' `* x+ i( Z z! w: O" n7 h
brq.mrq.cmd = &brq.cmd;6 b" ]$ ?4 D* @0 G
brq.mrq.data = &brq.data;: B7 K9 K! k- h. f& c
/ b! p f" W: @5 [) ~: A+ r) t1 W) Q
brq.cmd.arg = blk_rq_pos(req); // 解析指令的参数) H$ X9 ^% E) |
if (!mmc_card_blockaddr(card))8 C" O9 w; ]4 S! W- z
brq.cmd.arg <<= 9;
9 M3 I/ ^ A$ S% V) q+ w; q. m brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;1 q4 Z" D% e% ~/ T& Z0 l7 Z
brq.data.blksz = 512;9 h' Z" i0 @8 F- z: C/ o% ?
brq.stop.opcode = MMC_STOP_TRANSMISSION;! t& _' \. t) k: s
brq.stop.arg = 0;
4 V3 C& K; A, q5 P, P brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
& _$ ^( C) [, A( D3 l brq.data.blocks = blk_rq_sectors(req); // 解析数据块大小( U# H0 E6 ~8 k [1 Q. M
, O$ G8 C# k4 O: F5 B1 r/ K; C
if (rq_data_dir(req) == READ) { // 解析传输数据方向
$ m- o( p9 Q& k* r0 \% ]6 W brq.cmd.opcode = MMC_READ_MULTIPLE_BLOCK;; ?3 {8 d) | D
brq.data.flags |= MMC_DATA_READ;
" \. A: y* e4 D2 {* X } else { ^) E1 p; g1 _. \2 q) C' `, e- p
brq.cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
" k. B/ I% T/ s5 n brq.data.flags |= MMC_DATA_WRITE;6 y6 O2 w6 q+ k: ?* e
}2 n N) F9 z- ~0 v% Z! N9 ~
! |: b6 m! ?6 f- h7 X" ?& P- N( w. ] mmc_wait_for_req(card->host, &brq.mrq); // 调用core协议层提供的接口函数,实现数据间通信
I6 ?5 G- i' C' Q5 W: B' q3 u8 @: f, g& W& T% _; v# p- u& I
... ...
* x8 u* D/ o0 j+ Z- O9 |' C+ l- }% O8 X! |
}
0 `, ~+ W/ K- ?; X. R6 n& B: t' p, i( x8 f4 Z- ]
mmc_release_host(card->host);
/ B+ D$ v5 z W/ ?8 Q, a" |& T8 E+ G& d* v3 H$ }
}
# v% M. p. p/ w0 s( i. {7 p* W( Z/ t1 p' a
5 D# |5 ?/ h& G! y2 V! |' @. W5 }- o, w- S
5 f' \0 z, ^/ c6 l3 L5 }# e' Z& G |
|