9 ^* _) W6 {9 i9 N' `+ g9 u, _. _插入SD卡,主控制器产生中断,进入中断处理函数,处理工作队列,执行mmc_rescan。; e. J- y& a0 M \2 X2 {/ Q
& a9 d# |( l4 m/ |; p- o+ avoid mmc_rescan(struct work_struct *work)4 U2 e0 |) m5 j {9 k9 r
{7 V! S+ i! U' o/ F" V; n
struct mmc_host *host = container_of(work, struct mmc_host, detect.work); // 得到mmc_host的数据 n" B2 l3 m) E# y* i: J" h /*& `7 T. y7 t/ ?6 R8 K# D# v
* First we search for SDIO...! t9 P b/ g: }; A- k! W( i4 L4 O& f
*/, F% V& @) ?% P3 E6 O( S3 W
err = mmc_send_io_op_cond(host, 0, &ocr); & X! Z6 T i! G5 E: z if (!err) {' T( `, R- D Y+ @- B+ {
if (mmc_attach_sdio(host, ocr)), t" K H2 y* u
mmc_power_off(host); - c* i7 C7 b' C goto out; : `2 M; \- U: U7 o U# u }: H* F. v- y5 p
z. _7 L! G0 A m /* 7 l% u4 [3 D1 Q2 E; A. R( f0 e: E * ...then normal SD...$ T/ P0 n& l# K3 | a( T4 }3 q6 x! i
*/ 1 x# U. H2 @% w- H! o1 T err = mmc_send_app_op_cond(host, 0, &ocr); & h4 j) q2 q w7 Z$ x6 k: T if (!err) {+ e" x: I, f' o6 ~5 }0 H
if (mmc_attach_sd(host, ocr)) ' C6 {0 _/ Q: m r0 S, A# a mmc_power_off(host); 1 e& {! D5 W9 ^. M" ?7 y goto out;( ~/ z" x5 f N8 M; d7 ~
}7 f# U2 ^( H0 G" x* ^6 y, d
2 j4 [3 f% t- a( I! M8 O8 V /*' u. k7 Q( G) _
* ...and finally MMC.0 U& ?" a, V- ~
*/* ^* w# W% ~6 m3 y
err = mmc_send_op_cond(host, 0, &ocr);8 i9 Y" P* b$ m4 o
if (!err) { D) w$ b# {( J* I. w8 R. f if (mmc_attach_mmc(host, ocr)) ( o' Z- m+ h, j- q! h. W: v( o mmc_power_off(host);1 p, F! n# ~7 h% v! }+ i7 L
goto out; 7 ~- a0 e! [& E, j n) Z }: R3 |6 k- P/ ?/ Y" i
1 b& T0 ]# E' H$ t1 k" y0 i6 I mmc_app_cmd(host, card); /*#define MMC_APP_CMD 55 */ ?& a( V, g `/ A/ N% A
mrq.cmd = cmd; / ?- z+ { V, f9 ?! j cmd->data = NULL; $ o1 }1 n' g U2 C n & H- M2 u& M- D2 C1 o: _ mmc_wait_for_req(host, &mrq);" |/ a( H' L4 C: P
6 L2 ~1 G y. v# w9 @. J$ ]! w ... ..., q9 X; K% `$ [% s: e3 N8 t
3 a, |6 |, J' ]$ H
} - H/ t$ s0 a" F, l* q Z7 _& \! U * ]5 L" r; E" r; i1 H' n' N/ r这里的指令SD_APP_OP_COND只有SD2.0的协议支持,也就是说,只有普通SD卡支持,所以也只有普通SD卡能够成功上电。 4 H/ Y L% z$ {' h$ J" L8 p% F) t
- q! i3 X1 }4 V) q6 }1 B如果上电成功,就开始进行总线与SD卡的绑定,通过mmc_attach_sd(),绑定过程可分为四步, / z4 A0 g, H# i0 A& M$ B' O5 c# @) i6 L
注册SD总线上的操作函数 - struct mmc_bus_ops mmc_sd_ops * B# D( M i! M' J0 W设置主控制器的时钟和总线方式 - 通过回调函数host->ops->set_ios();, J" q [( B, T' G" I" A( j5 h: a
启动SD卡 - 根据协议,完成SD卡启动的各个步骤 d0 g2 _* Y3 K8 ^& t7 p1 k注册SD卡设备驱动 z% h3 B/ R4 W% B" O , ?4 b: i6 Z' Z( Z$ W5 ^1 p3.1.1 注册总线上的操作函数3 X# N' G. D A4 { ! j' O& c% {& H% }
int mmc_attach_sd(struct mmc_host *host, u32 ocr) * J4 E {# S A& E9 Z8 t8 w' Z{7 f+ X4 b- S! J
mmc_sd_attach_bus_ops(host); ) L1 N R, |3 R w( a & T9 f! A: ?: `: b7 \8 Q& V5 H ... ...& Q4 k/ s7 |/ W, E. ^! }
( i4 O# m' v; K- q& ^, w
} 2 _" y I+ r- F4 @ ; _ Y4 B8 ?) z3 D1 G) @5 ystatic void mmc_sd_attach_bus_ops(struct mmc_host *host) 6 ^2 G. j9 _" l9 t{9 J& T* w/ U& \
const struct mmc_bus_ops *bus_ops; # ~. w5 d- R) }1 d' m2 x : u% _/ N: q$ e bus_ops = &mmc_sd_ops;5 i2 M! [- v5 R! [5 C5 ^* k
mmc_attach_bus(host, bus_ops);% `2 s1 H1 } H; n& s( Y
}, w, V" n6 r( U
. h9 f. F; j8 w/ G8 O: ivoid mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops) ; L h5 V9 j7 [" C. U/ C9 f! d5 A{, `0 Y) u3 C3 D/ w
host->bus_ops = ops;8 E% ~" @6 K0 ]" a7 x
host->bus_refs = 1; 4 P+ M( _5 A/ f0 Y ~ host->bus_dead = 0;' d: k! W8 H* [! q' E. o3 D5 H
8 y( `$ @# K' W4 l1 h" c1 R} 1 J- d a1 ~/ e: s& _! W+ C " c7 D4 b' c, ]# ystatic const struct mmc_bus_ops mmc_sd_ops = {/ K/ w6 V. L6 w, o b
.remove = mmc_sd_remove, // 拔出SD卡的操作函数8 C+ S- B5 R; e+ r
.detect = mmc_sd_detect, // 探测SD卡的操作函数! w+ n/ s } c! U M8 c- L
.suspend = NULL, + N5 ~- F" F1 W& { ] .resume = NULL, . s; Q0 }/ W) W" V$ |+ n .power_restore = mmc_sd_power_restore, // 重新启动SD卡的操作函数( F5 ?- Z' w' ~6 i; N. }8 t0 O
};5 |0 z/ D. `$ E, `! y, J8 e
这里的mmc_sd_detect和mmc_sd_remove就是拔出SD卡所需要用到的函数,下文将详细讨论。这里需要注意的是,插入SD卡的时候, 并不执行mmc_sd_detect和mmc_sd_remove这两个函数,但是会注册它们,也就是说,这两个函数的功能已经实现,将来可以使用。( B/ ~- `4 x1 x8 q6 L
( ?7 M! H8 [1 z ?3.1.2 设置时钟和总线 * x1 r6 T( Q2 V0 D . A, c* ]: m% L& G$ @5 ^ int mmc_attach_sd(struct mmc_host *host, u32 ocr) ^2 Q7 P, i3 [; }$ i+ n7 s9 E0 o{. j5 e3 X( C1 c) k7 s
host->ocr = mmc_select_voltage(host, ocr); : {9 j* p5 O3 y1 v! X& M* M& ^0 w" l) U( D3 P
... ...' z1 @8 a' d% j
2 ~) _& e4 q `" a0 H} 7 o, ]2 X* r8 A% J3 z7 y) [ - ~+ `- J* p$ h/ i2 M$ y% z: r2 }u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) + y& I e. A5 \+ j. ^/ r{: |2 S: B/ k$ S2 C" f- f
mmc_set_ios(host); 5 L0 L' @% X) K8 g% `1 q0 K , V6 x- z0 ~# \8 Z* ?' O ... ...6 ?* n( u# {7 Y/ R/ `2 y
}% U- S0 [& Q( P
static inline void mmc_set_ios(struct mmc_host *host); ?0 V8 c& y" N2 B+ t
{. d, ]6 c3 g5 G& \
struct mmc_ios *ios = &host->ios;/ U; ? W: P& r
2 _1 e: i x) D
host->ops->set_ios(host, ios); // 设置主控制器时钟和总线的回调函数,具体实现由主控制器驱动完成 9 b* a J' {8 P4 n! ~* c) G}- r1 P& f/ z( f( I
从这里可以体会到回调函数的精髓:协议层里利用回调函数为所有满足该协议的设备提供统一的接 口,而具体实现由底层不同的设备驱动各自完成。注意到,之所以要定义一些放之四海而皆准的公用的类,比如struct mmc_host,就是需要通过struct mmc_host *host指针作为形参传到协议层所提供的接口函数中,从而得以调用。, g+ A" E3 Q P5 v" \6 x
; k; X# V6 `# J! w* n$ c7 h- G3.1.3 启动SD卡 0 t$ y: U3 C0 ^+ f% b' Q2 S7 `$ ]. T4 L' w) U) o
int mmc_attach_sd(struct mmc_host *host, u32 ocr)' D. F5 [( g N X8 E; j& U ?5 R
{$ z g# m4 K2 `0 A. X6 x
$ `$ O6 {5 A& Z; e7 b l4 z mmc_sd_init_card(host, host->ocr, NULL); 2 x6 Z4 a' R" c" H 2 V2 H; B5 |! U1 U! S ... ... 4 [& A5 ~: R0 i6 X4 q: F - |' U. L, y, e5 y/ D! s} 2 m5 G% z8 z9 o3 A7 X% t! B- c/ q* F5 S: @" w: M* R6 _/ W
mmc_sd_init_card主要完成以下任务, 4 t( W* Z# F7 y5 z- i$ f; B ( s; [4 b; ]3 t5 i9 a6 y5 K" Q) Q
; U) [# b$ r- @4 i* @6 y* }$ G. H8 O, u2 w Z' c7 e 3.2.2 bus_ops->remove() 5 ~, {' \' N( e6 u0 b* [$ e |9 ]6 I6 L( \/ m1 e拔出SD卡,其实就是注册SD卡驱动的反操作,实质就是执行device_del()和device_put()。) ~# l7 C% L( K$ ~
* c. Q+ s7 W% R& Lstatic void mmc_sd_remove(struct mmc_host *host) 3 t8 |4 @, a3 s8 E. C8 M{ ' M1 e {: a# L/ v" J; N& v mmc_remove_card(host->card);, h: t- P* r/ l) K: O$ h7 L/ |
host->card = NULL;& b3 s3 M$ v" g8 V) g
}: j( _ A0 n; c! ]
void mmc_remove_card(struct mmc_card *card)% x2 M+ R. I9 }* _! I
{8 h7 I( q- e: S! p' q
if (mmc_card_present(card)) * C; `# k l$ N# i device_del(&card->dev); 2 |9 F6 _1 r. C9 ~9 P 9 |: D( v1 b9 b put_device(&card->dev); 0 c: L3 d$ Z1 l% l& {8 X C; H} ' l& m2 c& l; y% R0 J. { 9 E9 M' u2 c U& ?4. 块设备 % r! ^! [: i9 U ]4 A1 _ ; b; {* Q8 M6 V6 ] f首先,必须知道为什么要用到块设备。在linux下,SD卡通过block块的方式(以512字节为最小单位)进行数据传输,它必须遵从块设备架构。在linux块设备层,I/O调度者通过请求队列机制负责对块数据的处理。 0 U( Y$ `! Q/ ]& V5 R3 z, Z, M. ^* ~
8 g7 v" a- {( s6 s3 n
& ` b5 _4 H8 `. R- \. N
SD卡子系统分为三层,主设备层,协议层和块设备层。块设备驱动位于/drivers/mmc/card/block.c,主要完成两个任务,- V# w/ [+ h! H1 |
4 b. W: n/ s% z2 J' q/ D1 C3 J
建立设备节点
通过请求队列机制进行数据传输 $ i2 y9 s( a: X! e' A+ X& L$ ?. _
* q" m$ D- [) v
, Q0 C# U' M1 z5 v% N' G插入SD卡,注册驱动成功,那么在开发板的目录/dev/block下会出现SD卡的设备节点。 . Z3 G0 S; |6 f3 W5 S: L3 `" C, K* t0 u
179为主设备号,定义于include/linux/major.h #define MMC_BLOCK_MAJOR 179 2 f; ]( z0 R, D9 h2 ?1 V m - q; f2 \$ Z0 p179: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)。 ) `, o, o9 d. I. t
& X9 j! R) y6 y( G( S" z# }下面通过对块设备驱动block.c的分析,看看SD卡是如何在块设备层建立节点和传输数据的。 ' t J& z$ V4 I 4 T: ?$ C6 B6 i# r6 ~: y8 U1 C' U4.1 数据结构 / l* b0 h2 ~, U7 v3 y: `- M7 i6 p7 m8 h8 ~: d/ X
每个驱动都会有一个数据结构。幸运的是,我们SD卡块设备驱动的数据结构相对简单,在mmc_blk_data里,主要有两个成员,struct gendisk *disk和struct mmc_queue queue。2 q8 |4 x) L! i g. I2 I L
& o }, d% X2 p0 H5 d' V. d
1) struct gendisk 是general disk的缩写,代表个通用的块设备,其中包括块设备的主分区结构struct hd_struct part0, 块设备的行为函数struct block_device_operations *fops,以及请求队列struct request_queue *queue等。 + [& G/ W4 Q) I1 p* ~! ~% k: s/ q* c# |2 c) D
2) struct request_queue 存放所有I/O调度的算法。* v( h. |* }/ S7 a
( b% X5 k) V4 C) ?; S3) struct request 请求是I/O调度者调度的对象,其中的结构struct bio是整个请求队列的核心,具体内容请参看LDD3。 . u9 X/ c/ ?0 g$ x O " p1 f! u1 v! w6 [4.2 块设备驱动+ t: d- ^5 |& y4 V9 n7 e
" Y. X' p! h3 r: k
首先浏览一下源码, * V0 Q- L" N8 [; e) S1 d 4 g7 r: F C- m, i! l P/ o8 D) `static int __init mmc_blk_init(void) % y( _# h- |( G9 p{' ]9 k8 u0 h- ^( Y! c
register_blkdev(MMC_BLOCK_MAJOR, "mmc"); // 注册主设备号(若注册成功,/proc/devices的块设备下会出现mmc)( R" e- T ]$ B- D$ Q1 y n/ z
mmc_register_driver(&mmc_driver); @/ T5 |; b# g; n- T3 N) V
return 0; r- j! ^6 y' m* ^$ x5 {} * m4 l9 `9 ]9 z) O' Y4 ~ C: B 5 h0 U& c, M. c4 P- ostatic struct mmc_driver mmc_driver = {& a; c, o. [/ i7 w3 J" p2 h3 x; D
.drv = {0 Y+ j6 `9 b7 ?
.name = "mmcblk", ; f# }5 r; n$ `) E, j0 e: M }, , o: ~4 ^6 w7 M/ Z, v: I! s .probe = mmc_blk_probe,; R2 {) N& U. Y. V: T6 O# K
.remove = mmc_blk_remove, ! h, r% v) Z/ h, [* a9 J .suspend = mmc_blk_suspend," u2 ]8 J2 N! W0 e, p9 y9 ^, Q
.resume = mmc_blk_resume, - d0 z) s/ H5 o A: ?};- T7 |# @) m9 y# s
/ B% V0 Z2 g2 m
static int mmc_blk_probe(struct mmc_card *card) 9 l# I, M, K/ H' ^& L! J{ - j8 e/ F, R. \' }3 I struct mmc_blk_data *md;3 V2 c) i5 v4 H& m) c( s* i
md = mmc_blk_alloc(card); ! W2 p: X. K- v- X mmc_blk_set_blksize(md, card);: G$ ^, ~' h! ]5 h/ l0 u
+ M# g7 c# K3 L1 B2 f5 a) N mmc_set_drvdata(card, md); 2 t2 X& Z) t" g( c ?8 b' m add_disk(md->disk); 3 G9 u* b1 Q) l t' k+ G3 F. B6 G) O& i return 0;7 u3 w0 K! L. Z' B( z8 \
$ l' @2 b% b* C' m ... ...4 b) s" ^# S- ]* ?
% I$ b3 I5 p6 r, n* @
}( O4 k8 a; H5 i; C$ r
r. e# a( h+ s6 B2 d' d: {- p( X3 x 4.2.1 设备驱动的初始化函数 5 J0 X8 }. ]; |* q: u . S" Q! G! m8 q) V6 G9 |2 ` 仍然可以将驱动程序的初始化mmc_blk_probe(struct mmc_card *card)归纳为以下内容, 9 e( w7 g- s* ?& E: E, k 5 F2 U5 s0 X3 s5 W6 J初始化设备驱动的数据结构mmc_blk_data,并挂载到card->dev.driver_data ! a7 ^" Q* i! J9 e实现块备驱动的功能函数struct block_device_operations *fops' X, M( y4 J: f; n8 O
注册设备,即注册kobject,建立sys文件,发送uevent等 2 C! j( O3 b4 J' C其他需求,如mmc_blk_set_blksize(md, card);5 o! G5 q( Q U$ b# C
1) 初始化mmc_blk_data , R1 q6 H% M) d4 C- T " k+ }& g1 D6 s, zstatic struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) # U/ @# K5 @- A3 |+ S- a9 _{/ o8 ~4 v E, H4 I8 {9 `
struct mmc_blk_data *md; . i+ m2 k; q1 y$ b. p1 j% f md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);, s( Z" n& b$ [$ r5 I
/ Y1 K/ g% A2 b) J; ?' d md->read_only = mmc_blk_readonly(card); - q2 _: \8 Z, ]$ w3 p8 t8 X2 i/ `" f8 n$ j4 S, j3 N6 u
md->disk = alloc_disk(1 << 3); // 分配了8个可用设备 5 f0 |) i% y' _0 N! m0 M1 l5 m# Z! l# k
spin_lock_init(&md->lock); ! y' M+ A0 E( N9 V1 v* A( {% ~, A/ H D md->usage = 1;8 Z1 ~; I" g2 d
ret = mmc_init_queue(&md->queue, card, &md->lock); ) R7 ^& _+ ~2 D. A% ? " c& o, |3 C0 g5 p0 Y5 Q, x/ E md->queue.issue_fn = mmc_blk_issue_rq; , W1 u# z! `) h md->queue.data = md;& h! M: R4 g; L3 q4 V
?8 x% }# ^6 @7 c( M
md->disk->major = MMC_BLOCK_MAJOR; 5 N7 g4 N+ }( l6 F md->disk->first_minor = devidx << MMC_SHIFT;! z4 c6 ~0 t7 b1 ?0 i# K, C
md->disk->fops = &mmc_bdops;9 d+ q) |6 P$ D, r D
md->disk->private_data = md;5 z: H' r1 a* j7 [( T; V2 _
md->disk->queue = md->queue.queue;1 w6 R0 E! E' h4 i- _
md->disk->driverfs_dev = &card->dev; & m3 w2 D& M9 p& N8 E1 v ' v7 v! ~/ ]9 Y4 A7 m" j0 k9 V blk_queue_logical_block_size(md->queue.queue, 512); 3 i5 S& R2 X6 z" T2 z8 P) i$ B7 S/ j9 F+ _7 {
... ... : y! q- r, j1 a' A0 S! T) x- p- F7 W0 [
return md;1 T' ?& s. ^5 `4 {
) h# R, u6 O t; w
} 2 D& C( ^& ]3 G* Z& s/ Y9 T W; c' U0 F' U" S; Q7 M! Z" O
完成初始化后,通过mmc_set_drvdata(card, md);将数据挂载到card->dev.driver_data下。 % y9 J- w- }7 y7 N/ K+ U: d 9 V3 j' L2 n0 f) z& m2) 功能函数 5 L$ U% H7 |9 \. C# ^# q, ~, ] + \ O8 o# z6 |; y! ~* Nstatic const struct block_device_operations mmc_bdops = { & _: ^5 r* M G/ g! g& _ .open = mmc_blk_open, 4 l2 |3 i( r+ b .release = mmc_blk_release,! W. ?6 p, U' H& \! |4 H
.getgeo = mmc_blk_getgeo,- g! r; L( [1 Y3 @# C' W
.owner = THIS_MODULE, : }6 u- K5 `: j& F& f2 t: ?: D1 w};8 f3 F5 \! S4 k) D1 S( q) p
4 ^ u$ j0 B* i* `! Ustatic int mmc_blk_open(struct block_device *bdev, fmode_t mode) : J; ^1 |$ S; t. z2 Z$ V{, K; `0 V5 l% {- X8 `! \( o0 u
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk); ( u+ r* g& R& }, t" O9 J o/ i* H ) {' L X5 m: C7 R8 r, s" R+ l& w d ... ...* C" k8 @- E- j" p: h
}0 t5 j" Z0 h+ ?2 H- ^
struct block_device {: u4 W4 v0 b0 A* d
dev_t bd_dev; /* it's a search key */, p/ L, I/ Q3 j4 o! X
struct inode * bd_inode; /* will die */ / C* c, r$ N1 m struct super_block * bd_super;5 [) O6 y& g9 Q+ c5 q+ o) [7 g# o
' U0 [4 n1 [. {% p9 o: P
... ..., v+ W# b' d7 e/ q6 F% T* k
};' r6 x6 n# d( ^( W( Q
1 H5 [5 ~8 W$ a, ~6 R3 @7 A1 o与字符驱动类似,通过dev_t和inode找到设备。+ r; g# i7 e- F, u* s& H
) x- W( a/ n5 \6 l
3) 注册驱动 2 Z. N0 R5 j& ~3 \! q: E/ I# E! f( @4 j. K S$ F
void add_disk(struct gendisk *disk) ; K! k- [; L( l4 }: d0 I4 @8 x{& T* v* p7 o/ t4 w
blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk); + X* b; F# a8 m$ _5 r; p$ M! M9 g register_disk(disk); 8 b8 b! P/ e( n- D, Y7 l2 o; m blk_register_queue(disk);$ ], s, R4 }; z; P0 o
8 `* T5 G: ` b @) ~ ... ...+ g- a2 s9 g/ K- b
/ h; C" K' v4 p6 r8 w, }) h2 g
}0 O" W5 A( E8 u% c
; R6 F7 G e2 `; Y( l h! {* \
blk_register_region()在linux中实现了一种利用哈希表管理设备号的机制。 ) _4 y6 z# K) \$ ]/ X3 V1 x D 5 T# |- ]4 F9 u3 a/ I5 Hregister_disk()对应alloc_disk(),完成对块设备的注册,其实质是通过register_disk()->blkdev_get()->__blkdev_get()->rescan_partitions()->add_partitions()添加分区,建立设备节点。 ' ?: Z. S- L0 k! N* i& }- @' p! l( q7 m) K7 a( s
blk_register_queue()对应blk_init_queue()完成对请求队列的注册,其实质是通过elv_register_queue()注册请求队列的算法。 * @* o1 g+ U9 E! C" o5 J * f8 G$ s/ e2 I6 d. `1 L! b8 Z关于块设备更为具体的代码分析可参看linux那些事。4 |' q1 n: k% |& q; b
0 q8 l; l/ i( @. s( z Q! @5 s8 A 4.2.2 请求队列 0 g, b9 d- A2 T) l* F% [' u0 d- J& o& P6 N
mmc_init_queue申请并初始化一个请求队列,开启负责处理这个请求队列的守护进程。 7 _( Y, M$ @1 c' |) |9 J1 C , K& }! R( r- Z2 T3 c- w: Wint mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) # n6 `1 `# u0 u{# l. p+ M' M- V( ~
struct mmc_host *host = card->host; ( U1 o$ ^3 E, `# y0 K \! _* M mq->card = card;$ k/ S" L8 C3 Z3 M' D
mq->queue = blk_init_queue(mmc_request, lock);( {& b' g& |3 Q3 |- d
7 o; I' X3 i* N" r) w mq->queue->queuedata = mq; 6 _( T! Y7 D5 V" L; F2 c mq->req = NULL;5 K" ~6 a( D: ]2 G' t- [* \4 N4 ~& g
8 }0 B# E6 I D
blk_queue_prep_rq(mq->queue, mmc_prep_request); // 注册mmc_prep_request算法* d+ U9 ~( v2 }7 u8 V- H/ l# B
blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL); //注册ordered算法1 D9 a! W6 o9 a. c9 B" c9 b# K( l& p
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");& T: b% o n' n( y ~
' d% U* `0 }4 X3 A
... ... % \0 x! ]0 O6 C* U+ x} 5 F0 o# X4 T1 S( Q- O ' Y( O: v( P3 T6 T* Y- z; K/ i1) mmc_request 1 V- H9 k- s) C g 4 U" k4 z/ A$ @; ^3 V4 A. k1 {; B它是处理SD卡通用的申请请求的回调函数,或者说是SD卡申请请求的算法。当CPU处于not busy状态,会寻找一个请求,并试图执行它。- |% V. ?, s6 }4 E$ X$ z2 M7 y
! o$ c0 [5 x9 A8 `$ ^" N1 o/* /drivers/mmc/card/queue.c */9 o: I. F9 p! C
" X" f& q9 E0 ?( e
/* ! Q0 d0 W6 [: E" F/ |0 ^1 f5 r * Generic MMC request handler. This is called for any queue on a . W Y/ E; i* U: E3 J; p7 L * particular host. When the host is not busy, we look for a request % ^7 O3 v' I$ M. L * on any queue on this host, and attempt to issue it. This may$ l' k" R; e; F- p5 z4 w$ m$ `& }
* not be the queue we were asked to process.: t% }* j# O& y! o) Z# p2 N5 o
*/ - J& ?, v& z, p, p! \- P8 c% Jstatic void mmc_request(struct request_queue *q); b7 s/ F( \1 A$ y7 `
{* ?' B! M& [6 J( ~3 K
struct mmc_queue *mq = q->queuedata; % L( q7 ~: P4 [, w% N8 g struct request *req; ; F& E8 N0 D4 G' l7 v7 G e# A7 I7 z& V d- M. ?" u
if (!mq) { # `0 l) i, s6 F9 T while ((req = blk_fetch_request(q)) != NULL) { // 寻找来自请求队列的一个请求req5 \( T2 [' j" d3 d4 `
req->cmd_flags |= REQ_QUIET; ( {3 E' Y, K) p# _- ~5 ^, Y1 \ __blk_end_request_all(req, -EIO); * P" [* q1 [/ A" p! j+ b+ _2 O } . F: B4 I" g( S! X0 z T& ]; ` return;* C+ z, k @9 a; w# u
}# J1 {) q- I# b
: @6 |8 Y0 u( F4 l- \ v" `9 t3 C if (!mq->req). I7 I, Q! p* u3 J* O/ k7 K
wake_up_process(mq->thread); // 如果队列里没有请求req,唤醒守护进程 0 E/ D; B4 W: m" w$ H}" S$ A1 M. O( L$ b$ u6 g
( F, A, [( o& p
这里我们需要关注这个处理该SD卡请求队列的算法是何时申请的,也就是何时会去申请请求,何时会去唤醒内核线程。 * b: B E( E2 V1 Z# \0 ?0 V# k 5 `8 F6 n0 N$ c5 ?! R* s用到回调函数q->request_fn有三处 + y& K2 J+ Z8 I+ q) R% [* b; G! p6 l( Q
块设备驱动注册请求队列blk_register_queue()
驱动程序出错,清空请求队列mmc_cleanup_queue()
实现请求队列机制的blk_fetch_request内部本身 & W2 v1 N/ c4 ]2 o# s, o
+ A9 L/ [ m3 ?+ D1 z7 A3 {! Q( W- \) r. e* z- ]& o4 f
blk_fetch_request()->blk_peek_request()->__elv_next_request()->blk_do_ordered()->...->q->request_fn6 V8 ~- S$ d. Q2 z8 {- `; h