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