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