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