EDA365电子论坛网

标题: 浪莎淘金----Linux设备驱动子系统 [打印本页]

作者: haidaowang    时间: 2019-12-5 10:13
标题: 浪莎淘金----Linux设备驱动子系统
Linux设备驱动子系统
) G# C' a! B# t- b/ T3 y9 O2 k3 Z  V/ _# Z4 `7 i
1. 总论
8 e: U( O: ?) N6 l* M/ h) p2. 主控制器2 V% \7 Y. ]! b! q3 e
3. 协议层! U* r' Y$ J" Y. \  k
4. 块设备
4 q6 _) u) }( Z+ D. ]* C$ F/ m
5 O( f  ?* |& r5 F/ c. E& X/ d) C; d  R  |5 m" K5 u: A, C
1. 总论  F+ a) [4 k( @: R# L; _
0 \1 P9 v0 ?, v1 n# |1 S, g3 ^
1.1 概念. s( J% _& U6 s3 Q9 X
* q8 X4 ?. L* s( D' X5 l. k
6 ]8 r0 j  e4 r( a4 [
1.2 分类
5 [, T- z* W6 G) |3 F) c8 H
8 _' y: e. l( C, D" H* P

5 X! m! ~! c0 }# ^8 E( O
0 D, |9 }0 H' }+ z3 r1.3 速度
2 T5 o' ^( o, i, f: m
" O9 `! q) H0 q! a5 X. e5 c/ j9 N6 v

) t: d1 p* N5 A. {6 T  r2 [$ M2 `1 r  n3 ]0 l
1.4 子系统代码结构

* F3 R8 N7 K  _' `7 Z1 l3 ]/ h/ T( y  A( c' g- y, n. z- z1 w( F3 t" @0 C
     Linux源码里/drivers/mmc下有三个文件夹,分别存放了SD块设备,核心层和SD主控制器的相关代码,可以通过Kconfig和Makefile获取更多信息。: ~  ]. K6 o7 k8 E/ J7 ~
6 B( d+ V0 C7 S( R

0 e6 f9 I& w: f; K( e$ N$ F0 g6 E2. 主控制器
6 X" t5 [0 t+ f8 a+ K$ w; Q1 Q+ e  F' T
    SD卡的控制器芯片,可以看成CPU的代言人,它为CPU分担了完成与SD卡数据通信的任务。
! @, w5 E3 g* p1 y, }) n( R* o& S- K+ D
2.1 数据结构
* W& J3 D+ `# U+ P' @" g
, G! a6 c! z/ d  s$ V- x+ n     以PXA芯片的SD控制器驱动为例,* f% {1 K! Q, ^; E8 R! C3 ]& Z* V
  v# g1 _# H5 B; w. a4 ]# g
struct pxamci_host {. d" z% P9 y- c; C: F  h5 [
struct mmc_host  *mmc;
0 L5 U+ ]; x# V2 ~: x9 j
8 g" T# P* u- ^/ N# K( d struct mmc_request *mrq;
- Z4 d9 `9 }* L( O" M struct mmc_command *cmd;% r) x5 \% U& P) [6 {+ s$ o# \
struct mmc_data  *data;1 J7 O4 C0 u$ L+ X) y% B+ u; N

: ]% d0 n& L& H2 X0 p ... ...
9 P/ v  |+ d7 F& e. ?};- U+ ?( M, p/ T) x
+ [( O7 z4 U! a. p! b
2.1.1 struct mmc_host
  M9 k+ p* {! \9 Z* n6 W* a  K7 }( f9 v1 B8 o! b0 D
     结构体mmc_host定义于/include/linux/mmc/host.c,可以认为是linux为SD卡控制器专门准备的一个类,该类里面的成 员是所有SD卡控制器都需要的,放之四海而皆准的数据结构,而在PXA芯片控制器的驱动程序pxamci.c中,则为该类具体化了一个对象struct mmc_host *mmc,此mmc指针即指代着该PXA芯片SD卡控制器的一个具体化对象。7 y" z8 A' }! r" d: `; P5 [( s, o
& i, o$ E* v: t* v4 C
struct mmc_host {
8 E- O6 K  f7 o  }) D/ h. t' q const struct mmc_host_ops *ops;     // SD卡主控制器的操作函数,即该控制器所具备的驱动能力
; C* I' u6 B: }! d6 M, X5 D% ^7 V% K0 ?8 P' w. v
const struct mmc_bus_ops *bus_ops; // SD总线驱动的操作函数,即SD总线所具备的驱动能力& m8 S6 Y8 Y' j: \$ z/ J
+ \* g" t, C( i4 p+ p& |
struct mmc_ios  ios;  // 配置时钟、总线、电源、片选、时序等; i6 I% o  @7 z, K

* p; ]% g$ l, R6 _' \3 V# d& w struct mmc_card  *card;  // 连接到此主控制器的SD卡设备( N7 r2 H# k3 V6 |0 F3 V* j8 T
    ... ...
# C: Y3 h  ]1 [/ L, `3 n  t};
# G' i- m3 o4 J* k8 y
2 \( C& c- Y/ m. V9 Q( F# y3 _9 Q3 j ( T  R" C/ j+ |+ k
! @( Z3 E% x) F5 x+ e
struct mmc_host_ops {
+ T7 Q. S* e0 | void (*request)(struct mmc_host *host, struct mmc_request *req);  // 核心函数,完成主控制器与SD卡设备之间的数据通信
1 h* J, U1 v0 K9 U void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);  // 配置时钟、总线、电源、片选、时序等. y5 l. W  z" W6 D8 t- ]
int (*get_ro)(struct mmc_host *host);# @. p) G6 ^& c! t' |$ n9 a( z3 L
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
0 Q5 m& J9 e, e' o; S};
, n7 t( M( v" m0 ]2 h+ I4 T& \- X) I2 C# j
struct mmc_bus_ops {/ O6 t4 c: s: w( N
void (*remove)(struct mmc_host *);    // 拔出SD卡的回调函数- R9 k! O. F: Q& ^3 W) W( j/ q# f
void (*detect)(struct mmc_host *);      // 探测SD卡是否还在SD总线上的回调函数
- H0 n, \8 j* `/ k% E) k) A. }/ ]8 E, q void (*suspend)(struct mmc_host *);
) q! L" T! O  r: O% E8 H0 B  i void (*resume)(struct mmc_host *);
) N5 u  z+ D  \3 N% V};
8 f% Q) l6 F6 W4 a
& w8 [) M# g9 S/ Z# v* B  W9 ustruct mmc_card {3 v2 L. h6 |; |* @# P0 }+ I0 ]
struct mmc_host  *host;  /* the host this device belongs to */, V1 [; R6 s. t3 [
struct device  dev;  /* the device */) [1 N: ?0 E2 K  w# y9 l
unsigned int  rca;  /* relative card address of device */
* i8 j5 {7 C! c, R* N( r7 k unsigned int  type;  /* card type */8 l1 O; I8 I4 n
unsigned int  state;  /* (our) card state */1 E# S. [% u+ }* Y
unsigned int  quirks;  /* card quirks */
6 |$ I; S9 n- U6 L5 T+ ^
/ u) f" a0 D( ^% r u32   raw_cid[4]; /* raw card CID */
" f6 T9 n# [( l2 S  x1 I u32   raw_csd[4]; /* raw card CSD */3 X* {6 n( F# ?( p) K4 @8 E
u32   raw_scr[2]; /* raw card SCR */0 R% G4 V. c! k6 F0 M+ f) ^
struct mmc_cid  cid;  /* card identification */
( n' R  I# A' F/ n8 j2 g struct mmc_csd  csd;  /* card specific */$ P# w  u& w) j* y! G$ s7 f: {
struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */
' W$ R6 N2 @$ b( ~0 C9 }! | struct sd_scr  scr;  /* extra SD information */, h' Q2 E3 u0 m0 r+ l
struct sd_switch_caps sw_caps; /* switch (CMD6) caps */
2 J  [% w0 N7 |# w8 V& x5 a% a
9 Q! r! h9 ~, d unsigned int  sdio_funcs; /* number of SDIO functions */
( T1 i3 t) V! N2 a: v' z3 X struct sdio_cccr cccr;  /* common card info */
0 {3 C) x, V7 U- b struct sdio_cis  cis;  /* common tuple info */
: p& h  X- f: i% G; o4 f   ... ...
7 Y; p& y* g( x: p};
( P5 C9 F$ Q1 Q$ H" l) G# gmmc_card结构体内的数据结构主要存放SD卡的信息,其中RCA, CID, CSD, SCR为SD卡内部的32位寄存器。2 v3 L$ O% m# a' K

5 U) Z+ L1 l# X2 P
' g: W; Z9 L8 \1 o 2.1.2 struct mmc_request. k' ?, I( a% Y. F! y

8 d( P% \' v: M) O$ A' T1 R      结构体mmc_request定义于/include/linux/mmc/core.h,它主要存放两大数据结构的指针,分别是cmd和data,顾 名思意,一个为指令,一个为数据,也就是说,mmc_request结构体存放了进行主控制器与sd卡间通信所需要的指令和数据,struct mmc_request, struct mmc_command *cmd, struct mmc_data *data三者之间的关系如下所示,
9 v* A- d1 {2 i& P4 P. E6 u7 o1 g0 i4 z) [
struct mmc_request {
) W0 ]9 n+ X1 _+ w: e struct mmc_command *cmd;
9 t! E6 X1 c& F; ^ struct mmc_data  *data;! A9 n! @9 V% j! h# W$ w6 X
struct mmc_command *stop;9 L# v+ E" S  z, `$ q  O+ |

$ l8 d# r/ p8 e! ? void   *done_data; /* completion data */
/ Z) J6 h4 v9 _6 t3 K! E9 J void   (*done)(struct mmc_request *);/* completion function */+ H) P* |4 i: X0 X. W
};6 n6 W, h3 |* ?$ H* P- a

& d8 t; q0 s6 W1 D   说到结构体mmc_command和mmc_data,就必须说说SD卡的协议了。
3 n% _5 M' A$ s* E% m# U6 w- F! W' H# D, M2 t  O
   1) 物理结构- o; B4 C9 t- S. Q4 G3 c: B9 M: c

$ l1 q/ I- r+ j     SD卡有9个pin脚(micro-SD为8个,少一个接地pin脚),如图所示,3 S  O0 h/ S( j
& h$ Q$ G1 C! f

4 p0 m) [+ S0 v/ ]8 b1 j& }5 g- Y, e7 u
   SD的数据传输方式有两种,普通SD模式和SPI模式,以SD模式为例,9个pin脚分别是VDD,VSS,CLK,以及我们需要关注的一根指令线CMD,4根数据线DAT0~DAT3。& y. r2 E5 t2 O
7 e$ r# S2 L0 H) L
   2) 传输模式+ r5 Q! ]7 I2 a$ O
- j  P0 x- J2 ]2 c; ?! r
   首先由主机向SD卡发送命令command,等待SD卡的回复response,如果成功收到回复,则进行数据传输。其中,指令线和数据线上传输的指令和数据都要遵循相应的协议格式。
8 n( L+ Y1 G" E& T' d* t6 m6 m/ U( E6 H: k2 \1 u( Z) q
/ }0 d' b9 a' \& e( A

/ S; K$ p5 W+ v( q! O! t   3) 指令格式
9 |" o, R# O5 Q/ \4 _3 A% J
$ n( n. d9 O$ L8 N6 F  5 O4 _6 U6 l6 ?) L; ^& v3 L
2 e( Y/ |- }6 h/ L7 }( e
   一条指令command共48位,其中command index指代这条具体的指令名称,argument为该指令的参数。& r2 I: j& V! C- V- t$ q! e

8 I& X& J, r" K( A6 c+ u$ W   一条回复response根据不同的指令有几种不同类型。7 |9 J' W5 J+ q

1 F* ^, z. P: h6 Tstruct mmc_command {, I; T% @! S1 }
u32   opcode;            // 对应command index
3 G: @2 K: @, ^- q% \ u32   arg;                  // 对应argument5 C( i$ D4 d" v, ~! |
u32   resp[4];           // 对应response4 f% [; C( N% D- D8 D
unsigned int  flags;  /* expected response type */# b/ R; C  f( K4 y, n

/ Z: L  I) {- d2 D- t0 f- a ... ...
9 A4 T) K( l: _* k4 C" @
9 f# O5 ~( n; [# Y$ d2 [ unsigned int  retries; /* max number of retries */
. J2 w) y' ], W unsigned int  error;  /* command error */
& V' o" b  K+ \. f! s
' v" ^4 V' O& `' U struct mmc_data  *data;  /* data segment associated with cmd */
  h8 {5 ^7 w8 I* N. }' q6 v struct mmc_request *mrq;  /* associated request */
9 n) `. U6 Z$ W1 z};
. [% }) u8 W3 S$ E& V) a1 m
' K! ^3 ?! N0 N' S0 T; p   4) 数据格式
3 e* T5 m8 M8 d. z' C! M
& l' y: l) d3 v0 B2 ?   数据传输按数据线可分为一线传输和四线传输,按数据大小可分为字节传输和块传输(512字节)。
4 x, @4 ^+ C) |% D& }
3 Z, b$ ]% q: ]4 w: [. qstruct mmc_data {, o5 c  j0 o: C; r# L- f% N
unsigned int  timeout_ns; /* data timeout (in ns, max 80ms) */6 K: W) r& b- Y! {5 j+ C
unsigned int  timeout_clks; /* data timeout (in clocks) */; a* U6 P/ V- e1 h
unsigned int  blksz;  /* data block size */
9 ?' n# a! |, ^# g4 \! D* m unsigned int  blocks;  /* number of blocks */" r- R8 q9 @% i* A( b- ~
unsigned int  error;  /* data error */" Q# Q+ c0 T; W! Z
unsigned int  flags;
/ x" x2 p8 r6 R# @) Y  h( b! i6 t/ n0 ?; z
#define MMC_DATA_WRITE (1 << 8)$ w/ p7 l! T; X4 W8 O
#define MMC_DATA_READ (1 << 9)
$ K+ h# i' r  k#define MMC_DATA_STREAM (1 << 10)
" j& _( A2 @0 Y2 W! U4 H
6 t2 Q  G# }# @9 j) m# p2 T% Y% \0 b unsigned int  bytes_xfered;; j' n4 X7 G2 ^0 x$ `9 e
: R; g0 x; V+ o
struct mmc_command *stop;  /* stop command */- z, f! t; Y& J7 G- O+ k6 r- v
struct mmc_request *mrq;  /* associated request */) o+ V1 J% _2 j
2 K: K0 D: M2 f' _
unsigned int  sg_len;  /* size of scatter list */
: x/ r0 B, e2 s. d: O8 z( c" | struct scatterlist *sg;  /* I/O scatter list */
+ k" L4 ]2 i  I# \};
# R, T5 v3 v1 ~4 O, D- _" i+ t- O$ f/ p! B2 q9 s- Q; ~) L
2.2 驱动程序
" }3 i, J: p; @+ R8 U
' D7 B' F4 |; D2 l   系统初始化时扫描platform总线上是否有名为该SD主控制器名字"pxa2xx-mci"的设备,如果有, 驱动程序将主控制器挂载到platform总线上,并注册该驱动程序。/ }& ~1 H3 _! I. c4 S, H

* J4 d5 d3 H; f; K( W* i static int __init pxamci_init(void)
; p% j9 f6 [& N% \) B8 L/ `" d$ k{
, N3 W0 g) b  [* w6 U2 E" Q3 O; d! \3 Y* [- y- s7 {
  return platform_driver_register(&pxamci_driver);8 T; {7 e5 ?) A3 q1 g
}
% W7 E" u3 ]  O2 s
$ T! c. i; |/ \static struct platform_driver pxamci_driver = {
% k0 y) A# o/ t" E0 s .probe  = pxamci_probe,
" L2 ]- X* N% o' I9 n! c2 ^' J .remove  = pxamci_remove,- W# B0 v/ X" a- e6 o4 F7 ]+ T( [
.suspend = pxamci_suspend,7 `% D3 \2 }* p4 r, ]9 C
.resume  = pxamci_resume,( _8 R# ^; H/ |# }
.driver  = {
% y% Y0 s. ~* f. s" ?  .name = “pxa2xx-mci”,
' @' Y7 p3 V& s- P) I; Y5 d* h% c  j  .owner = THIS_MODULE,4 A- T& p& x8 j. g0 G4 ^3 u6 i# {
},# M6 q( ~1 j) s/ ~
}; , X# ~- [+ D7 D3 G! ^! c" P' s
' _7 p7 o' v+ j2 n$ _8 \4 U
其中,remove为probe的反操作,suspend和resume涉及电源管理的内容,本文重点讨论probe。; U6 d! o+ p4 I5 H4 P5 K- {
* G4 W0 J+ e0 c8 b5 m
SD主控制器驱动程序的初始化函数probe(struct platform_device *pdev),概括地讲,主要完成五大任务,
: Z& J0 k5 L9 K( c9 U+ F3 Z8 L3 ]6 }8 ?3 B  j+ U# b4 r( S
' z2 d, n  x4 D7 }5 w

1 s( D. x( H% C2 U3 P# d3 E; l5 Z2.2.1 注册设备
1 q* {( a/ D9 {% ]% k
7 \% D" m% _( E3 `1 G    对于设备的注册,所有设备驱动的相关代码都类似。
3 A1 d* d( k1 K6 g% Z3 D
1 [- L- c& ~7 Fstatic int pxamci_probe(struct platform_device *pdev)
) X/ T/ G  C! `. n* v- |" i  k5 W: f9 p) T! [- m4 K
{9 L1 \1 g* K, p9 P3 r  A1 y
9 {* N. `; L# H4 S3 f! g
  mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
! W+ @$ S& |; r# h6 Z" _
% x4 r! h2 u. ^7 `5 w- Q7 R  mmc_add_host(mmc);
& y& X' }5 J' t& O3 T* s4 |
9 Q8 {% ]  K7 c9 S+ e- u  ... ...
% ]: W7 Z& c0 l$ H
2 P5 H" H9 A. c/ _3 I8 d; K; ^}
7 c- i. D) x% ^  `: H. _4 |: v" P: ]) z! i5 E; `1 m
这两个函数都由/drivers/mmc/core核心层下的host.c负责具体实现,
1 B3 I7 f5 H, K% M- n2 d0 D% U0 I, c8 t. m2 s3 m* O3 q/ p: a
1) mmc_alloc_host
) v- I, h  L6 B
3 n  C  X: z2 L& X6 E& v1 R为主设备控制器建立数据结构,建立kobject,并初始化等待队列,工作队列,以及一些控制器的配置。其中,INIT_DELAYED_WORK(&host->detect, mmc_rescan);将探测SD卡的函数mmc_rescan与工作队列host->detect关联,mmc_rescan是整个SD子系统 的核心函数,本文第三部分协议层将对它作重点讨论。, n8 W  b+ u/ Y. c' r

* K% k: y; L& Mstruct mmc_host *mmc_alloc_host(int extra, struct device *dev), u/ k+ y4 }5 b4 M. e; h1 _
{
/ h6 Z9 x3 }2 |. h! o$ }' {7 c& G% X& R0 ~' |
/* 建立数据结构 */
5 I9 c2 n- T# n8 X. V( R* Y& j9 |  c
struct mmc_host *host;# a: z( }. [& f0 ?0 M, ?! a

0 @: }- g9 k$ _  f3 j& O" X" N host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);/ {! k2 R+ v: n8 W4 q* D
# o7 g0 c, p) J' ?' `8 R# O
/* 建立kobject */
' v( a" h+ o2 ~1 u, n2 C+ N+ J, R
+ o% d3 D3 T, h# ?$ a: G host->parent = dev;: r( O; M1 J+ ?) t) k: g9 _$ _0 a' M
host->class_dev.parent = dev;
6 J1 @) s# m+ o/ D7 [9 O6 R: |; n host->class_dev.class = &mmc_host_class;8 r: S' w$ n0 Y
device_initialize(&host->class_dev);
; C" [9 ^, D8 _8 V2 B! n3 t( g
/* 初始化等待队列,工作队列 */: j8 Z. g; q4 R0 S+ v! y' p8 o- M

5 W6 Q; ^* Z) J3 m2 R4 } init_waitqueue_head(&host->wq);5 g$ l/ h( B; L- p1 C8 e( c
INIT_DELAYED_WORK(&host->detect,mmc_rescan);
' I- Y2 o5 B# R8 Y0 Y2 ]0 w+ n4 I3 f/ n, F! n

3 _5 \$ w' J. y/ r" a; [- y0 D. z# c$ F4 X
/* 配置控制器 */
: c# Q4 T1 l8 x0 _# g3 @! e
) I, B) a  K1 ^! H: ^9 s- e$ O host->max_hw_segs = 1;( B6 j8 [9 S6 o4 o$ ?. f
host->max_phys_segs = 1;1 r. `- K" n$ q) V

4 A( q8 d3 k' n3 r" d0 k ... ...2 b/ O6 b3 v: \: \9 X
return host;
5 A* b  x5 [9 w8 s/ h, q$ e2 u* J}8 j# j# E1 A' q' c! I0 O

; F2 C% R5 X3 E2) mmc_add_host
* [: c5 f1 ?& ?8 P; x
" A1 s8 M4 q1 u: J3 L完成kobject的注册,并调用mmc_rescan,目的在于在系统初始化的时候就扫描SD总线查看是否存在SD卡。注意到这里的工作队列的延时时间delay为0,因为系统启动的时候不考虑插拔SD卡,关于这个delay将在下文讨论。5 `) \) T9 A: v! ^5 N1 n+ H

& ~1 j- {$ o/ g3 X/ g0 V: Mint mmc_add_host(struct mmc_host *host)' ^6 K8 m* k. Y9 v1 C# z
{
3 j3 h( q4 S; o, Y  device_add(&host->class_dev);3 o; i3 W8 d: @8 W
  mmc_start_host(host);/ Q3 Q, d7 ]. p: z
2 o2 C# t/ u$ B1 l* d+ L  x" h; \0 K
  ... ...5 a5 S% d9 i8 j' Z" t) g

) l7 r4 i! ^1 x}, T. v" K. ?1 i8 S/ }8 ?

+ h7 }* m/ j& _" Fvoid mmc_start_host(struct mmc_host *host)
0 j5 w9 V! H, [* ~{
' V# ~2 Y5 o1 c4 j2 z/ r mmc_power_off(host);
% e$ A" v% p9 c& j) H mmc_detect_change(host, 0);2 [4 [9 \' X1 ]4 X0 O1 u2 ?
}
2 E: q, F$ {1 }% q: Z' s" a2 o- ?
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
# R7 P" h7 |) @$ D{
% z; e4 A& b. G% h2 ] mmc_schedule_delayed_work(&host->detect, delay);
: u* R  N0 |' N$ K( V% E}) D0 v+ c: U1 {2 p6 r# `0 h
) ]2 u4 x: O2 i" c# `+ Y
static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay). ^$ W: _9 n1 R( ]# o+ }; K
{
" |* Z# X0 o: Y4 H1 S' Z1 m; L$ B wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ * 2);
6 O! U3 w9 o8 @8 n& |5 n$ b, H return queue_delayed_work(workqueue,work, delay);6 G1 |- p7 T2 B/ a" ~! t
}
+ i$ k. Z. `; p  k; z$ u( K1 h; {$ j# z9 B$ n. ~
2.2.2 为设备赋初值
1 \$ [0 N/ s# o8 v  ^4 r
   其实,整个设备驱动的probe()函数,其本质就是是为设备建立起数据结构并对其赋初值。pxamci_probe(struct platform_device *pdev)主要为SD主控制器完成时钟、存储等方面的初始化配置,
% b. P; W* B0 ostatic int pxamci_probe(struct platform_device *pdev)
7 C9 \6 x/ _9 r1 v1 \5 R+ K( J{5 Y, f# R' O) u! o3 z& O7 ~
struct mmc_host *mmc;5 L2 H1 S" c7 \( \2 U2 d
struct pxamci_host *host = NULL;  X( T: Y0 F+ H0 `

8 [' |6 I- g6 A) f% `0 ` mmc->ops = &pxamci_ops;6 S3 E: y# ?  s3 I0 r
mmc->max_phys_segs = NR_SG;! k; m  P5 @/ I) @+ U8 E# R
mmc->max_hw_segs = NR_SG;
$ @/ u% ]8 a- L. U( x mmc->max_seg_size = PAGE_SIZE;
9 Z, M- {$ r# ]1 l# X6 X0 r4 C host = mmc_priv(mmc);
4 n0 a  @) O) R. j/ S0 C5 B host->mmc = mmc;
% B# h0 b, }) ? host->dma = -1;
* p( G- C1 ^: w/ m host->pdata = pdev->dev.platform_data;( x# `; H, I! e2 m& w1 [: K5 _
host->clkrt = CLKRT_OFF;" _6 @3 r4 w6 Z9 K
host->clk = clk_get(&pdev->dev, "MMCCLK");  [3 [& j: N- p8 V# a: j# w1 I5 S
host->clkrate = clk_get_rate(host->clk);$ e- f$ ~( m% x! H, G+ w" f$ G2 ?* W% |
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
6 e. t0 p% R( Q0 j" v host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);" v: X' v# K7 f# G! z% k4 A
host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, pxamci_dma_irq, host);, {3 l6 P2 \# b6 W6 Y
... ...( C: ]- a2 ?$ T+ `# j% G
}& j" M7 ^$ Q: ]/ s/ Y+ Y; H
完成所有赋值后,通过platform_set_drvdata(pdev, mmc);将数据挂载到pdev->dev.driver_data。
" `3 A/ ^& [* w所有赋值中,我们重点关注从platform_device *pdev里得到的数据。platform_device *pdev是在系统初始化的时候扫描platform总线发现SD主控制器后所得到的数据。
) x+ }% Z# O, E  q1) 得到platform_data数据
0 I. ?2 K/ ^$ L9 W  \& I先看看platform_device的结构,
# c3 e3 e0 t; `% U% e: xstruct platform_device {2 y. X2 Z$ @! H7 W5 k
const char * name;
! p  F# U0 u  B. o$ I; n, K int  id;
( ~; P" ~. F3 o6 b struct device dev;6 G# \7 l( h  v, V' s5 m. g5 g
u32  num_resources;
  r" [  Z0 _3 m5 [: I1 @* L' | struct resource * resource;
+ _2 k. @, W- A7 f};
6 n( o5 `+ f0 |* @! L系统初始化的时候,已经为该SD主控制器的name, resources等赋上了初值,具体内容如下,
! D# H$ A  @3 tstruct platform_device pxa_device_mci = {
0 J5 O3 M' {( _$ v1 e( k .name  = "pxa2xx-mci",
; U" O6 ^2 J5 W. g3 ^2 j  j+ q3 ]- O1 i .id  = 0,8 O( ^6 u3 h: k# e
.dev  = {5 R% q/ m3 s' V+ _# n/ h/ _9 r
  .dma_mask = &pxamci_dmamask,
+ B* F% w: u: C) b) C! ~  .coherent_dma_mask = 0xffffffff,
$ K$ O% k* z3 @3 p5 e },
4 Q' e4 w" p- a; f/ t! ` .num_resources = ARRAY_SIZE(pxamci_resources),
. ~' D" w: X( X1 \  [* \: Q7 _% Q .resource = pxamci_resources,
* \( M( B) n6 h; A) X" c5 M};
- x8 o$ t3 P& ^& n- U1 \$ Ostatic structresource pxamci_resources[] = {& Z  @! G, b  H" y$ {/ \4 q, C; [
[0] = {: P0 \: B) M1 a4 _; B8 ~
  .start = 0x41100000,
/ a" `6 v" Z) E: V# k+ z  .end = 0x41100fff,, B. k# c" l( b; b6 U" o" N
  .flags = IORESOURCE_MEM,  // SD主控制器芯片的起始地址0 t! G, C# v# a! R5 L* b5 W$ A: ?
},- L- _$ g: h0 a& k" z  ]2 W/ P
[1] = {5 o3 N' }! X1 d+ ~1 G+ `
  .start = IRQ_MMC,          /* #define IRQ_MMC  23 */
8 s0 D# a) P' E9 ~/ q& t  .end = IRQ_MMC,, t) F+ ^! _, ~+ j! p8 P0 i
  .flags = IORESOURCE_IRQ,  // 申请的中断号$ s  f! M0 p' ^( k
},0 B6 S- O! L% x+ O. ~
... ...+ m( D  A9 o2 r8 I( G9 D& O
};
; w6 v4 T9 J* [! f$ K  需要注意的是,platform_device数据结构里的name, id, resource等是所有设备都用的到的数据类型,那么设备自身独有的特性如何表现出来呢?事实上,结构体device专门准备了一个成员platform_data,就是为了挂载设备的一些特有的数据。(注意与driver_data相区别)
& H8 J9 [/ d7 u. D% R* k+ [structdevice {$ f% j+ L" e5 K0 p( [
  void  *platform_data; /* Platform specific data, device core doesn't touch it */" T; M: u' }7 P& x
  void  *driver_data; /* data private to the driver */
- g; [+ u9 L/ }, V  ... ...
5 {8 M" f3 q6 h7 ?% _5 Y5 V& ?}
" r+ i/ ]3 S7 S  看看SD主控制器为什么会有这些特有数据,* d, L+ M: _! M9 P
static struct pxamci_platform_data saar_mci_platform_data = {! ~3 y% A) d# ]6 h/ D* y7 j8 I
.detect_delay = 50,
9 \1 c( e! J! C7 h4 a" g .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
. d% T9 J" ]9 U' z .init   = saar_mci_init,4 O. w) C  J3 I! d& L$ F/ a4 e
.exit  = saar_mci_exit,$ P4 l+ c! q- x0 N/ e3 v+ f! r
};
( d$ I! w$ `1 q/ p2 N  q, V-> detect_delay
+ ~# `9 p/ F6 g& F' q# s. O   就是刚才提到的工作队列的延时时间,设置为50ms,由于各种SD主控制器芯片的性能不同,这个值可能会变化。那么为什么要为工作队列加一个延迟呢?首 先,当插入SD卡之后,SD主控制器上的探测引脚会产生一个中断,之后调用中断函数里的工作队列,然后执行工作队列里的mmc_rescan去扫描SD 卡,为SD卡上电,发送地址,注册驱动等。考虑到插入SD卡需要一个短时间的过程(有个弹簧卡 槽固定住SD卡),如果没有延迟,那么插入SD卡的一瞬间,SD卡还没有完全固定到主板上,系统就开始执行mmc_rescan,那么就很有可能在为SD 卡上电、发送地址的过程中出现错误(拔出SD卡同理),因此,必须要有detect_delay这个值。2 L2 _0 |0 T4 b3 _' z8 }2 Q) t

2 X' W: H2 a; A$ ~-> saar_mci_init! p3 w, k  n0 i! D. U; i
这个函数为SD主控制器的探测pin脚申请中断,具体内容将在下文中断的一节中讨论。) U1 `: J- |5 _
static int saar_mci_init(struct device *dev, irq_handler_t saar_detect_int, void *data)( B+ B+ F4 }: c1 q
{
0 \; ^  O7 y: }8 \ struct platform_device *pdev = to_platform_device(dev);
" d; o) h5 d/ e7 H6 q/ h int cd_irq, gpio_cd;  // cd - card detect
7 Q5 i" t4 _$ O 7 c( i, n3 Q% o( N, ]% r" _/ G) h! U+ l
saar_mmc_slot[0].gpio_cd  = mfp_to_gpio(MFP_PIN_GPIO61);  // 将GPIO61设为普通GPIO口
8 C, l1 l( i% w7 m) l8 y$ ^ cd_irq =gpio_to_irq(saar_mmc_slot[pdev->id].gpio_cd);   // 将GPIO61转换为中断号6 p( \6 v+ ?/ A6 Z8 s# j5 A5 c
gpio_request(gpio_cd, "mmc card detect");  // 申请GPIO61* H/ R/ K' u5 o9 ]' Z
gpio_direction_input(gpio_cd);  // 将GPIO61设为输入类型+ i: x" R! X) }# x- `" n, O4 P
request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "MMC card detect", data);
9 Q9 C& n/ M) U5 I ... ...
- J/ y' X1 R7 Y: e' T6 {; N}
7 b1 |" L6 R- N( Q得到SD主控制器特有数据后,将其挂载到dev.platform_data下,并最终完成对platform_device *dev的注册。
9 N' J! K. G3 F/ D% d$ Lvoid __init pxa_register_device(struct platform_device *dev, void *data)
' Z8 j; @1 j3 r- ~& C% q6 w; o$ {8 ?{0 U  O% x2 E* I1 s7 c+ I; e
dev->dev.platform_data = data;
0 n, V: I# U. \ platform_device_register(dev);0 k( w' p4 \' ]5 c7 [
}
* r8 e/ R$ r) P* F6 [2) 使用platform_data数据8 ^- q5 l. f; w3 @4 m
  下面就看看SD主控制器是如何使用这些在系统初始化的时候就已经得到的platform_device的数据的,+ }: e) Y7 @5 e. S* g! M( X
static int pxamci_probe(struct platform_device *pdev)
9 I" a9 ?2 x: A{) k) a- X- {4 @
struct mmc_host *mmc;
4 r) n; s2 \- |2 D+ U! R% w! k struct pxamci_host *host = NULL;
, W  A( c% N, ~* A* F struct resource *r;
' e0 V2 R3 v$ {" l( |. ^  n7 A6 @" S9 d int ret, irq;+ r5 E  u1 B$ x
r =platform_get_resource(pdev, IORESOURCE_MEM, 0);    // 得到控制器芯片的起始地址
0 v, ]. M* P6 B1 l, c7 a! t r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); // 为芯片申请4k的内存空间! q, }6 f, K8 S6 G( E) D; k( o# h
irq =platform_get_irq(pdev, 0);  // 得到芯片的中断号- X" z& Q2 u) S# X9 u
host->res = r;& z5 ^3 V0 \( f3 W' E: y0 J
host->irq = irq;7 I2 u) x% [* u
host->base = ioremap(r->start, SZ_4K);// 将芯片的物理地址映射为虚拟地址" x8 v' _1 ~# }& \
... ...+ c% t$ P& V$ M' s
}
: j: m7 q& A9 i/ F

2 V1 [5 l5 \6 S3 f  o0 D9 @2.2.3 设备驱动的功能函数+ C) P' |/ y3 Q% N# W4 ^9 M
, ]3 [  p' U- q3 G
   一般情况下,设备驱动里都有一个行为函数结构体,比如字符设备驱动里的struct file_operations *fops,该结构描述了设备所具备的工作能力,比如open, read, write等,
8 A, k1 n2 C: z1 L" |struct file_operations {6 l; I" I" ]' |5 A' Z7 ?
struct module *owner;; k! i) R) Q  N5 H& G8 J& H: z1 M8 Q
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);' e7 G8 r- T; ~2 T) J+ s3 t% v
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);0 }% V, B: K2 e% H& D$ s
int (*open) (struct inode *, struct file *);5 D. V+ p. q2 Z" e% T( l5 C' j
... ...' r1 ]" Y. {& f
};
* Q% B  ~0 B$ v$ \$ o& N  同理,SD主控制器驱动程序里也有一个类似的结构struct mmc_host_ops *ops,它描述了该控制器所具备驱动的能力。+ D. d- t# C4 b
static int pxamci_probe(struct platform_device *pdev)) C* Q6 D% X" x; k0 V8 L
{
% A/ ?: _# v0 B6 k! @+ f) ?1 Y( h& I9 _  mmc->ops = &pxamci_ops;% G) \  D- F% u" M  `$ |! m7 G
  ... ...3 |7 n( l7 [$ @  e: l; B% ^* K
}/ ]. }0 Z& |* f5 X
static const struct mmc_host_ops pxamci_ops = {
" q' p4 W0 \% j( U2 N .request  = pxamci_request,
# s( ]& V+ b. K' m/ [+ H .get_ro   = pxamci_get_ro,
; _* K8 O  M# P .set_ios  = pxamci_set_ios,; ~1 x! u6 I# o+ y8 \
.enable_sdio_irq = pxamci_enable_sdio_irq,# C! }9 I* O4 c: P2 b
};
/ l5 B' L5 N$ C; b" t- i其中,(*set_ios)为主控制器设置总线和时钟等配置,(*get_ro)得到只读属性,(*enable_sdio_irq)开启sdio中断,本文重点讨论(*request)这个回调函数,它是整个SD主控制器驱动的核心,实现了SD主控制器能与SD卡进行通信的能力。4 h# c( g7 X$ ~8 g3 u; b
static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)  L) ~5 v* `/ z- d9 k- U' ^
{
3 u8 d' Y. R+ O) M7 ^8 I$ b struct pxamci_host *host = mmc_priv(mmc); unsigned int cmdat;: Z& d3 B/ A6 {/ D

6 `! T5 y/ H) C1 K. { set_mmc_cken(host, 1);
) p0 n7 ]7 A8 x/ Z6 m' q5 n8 u7 E) X host->mrq = mrq;0 P" g  |5 e+ \+ x7 j8 C
cmdat = host->cmdat;% {2 A7 _6 Y. {( u6 V( _% e
host->cmdat &= ~CMDAT_INIT;7 q2 P7 V1 ?8 t7 ]) ?
if (mrq->data) {: @3 i$ |# g; {! P6 q# e
  pxamci_setup_data(host, mrq->data);7 {/ g; s1 S" `: I
  cmdat &= ~CMDAT_BUSY;6 X' `2 U2 O+ N. m, ?
  cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
& r& Z- c+ f9 z7 _" Y2 j4 c: C! o. \  if (mrq->data->flags & MMC_DATA_WRITE)
" j* I. Z4 P* V+ ^2 b. ]+ i  M   cmdat |= CMDAT_WRITE;7 t4 A, G4 |; ^8 M; O9 o- K$ B4 l
  if (mrq->data->flags & MMC_DATA_STREAM)
) I+ J9 ^8 |$ `# g/ s   cmdat |= CMDAT_STREAM;, L# n8 b# Q, x. ?
}) C+ a, X7 o2 r+ v% e
pxamci_start_cmd(host, mrq->cmd, cmdat);
! {  ~2 w5 _, H! G9 H- l* f* r0 n9 X}
+ j8 o3 C4 T" u( G; w, K" r! \其中, pxamci_setup_data()实现数据传输,pxamci_start_cmd()实现指令传输。) H5 |! _/ q9 |# y8 E! Q
至此,我们必须去接触SD主控制器的芯片手册了。
8 d, A- x$ P% ^7 q% |6 e( W
1 k3 _# E, a8 T! g  J7 S2 b首先,SD主控制器由一系列32位寄存器组成。通过软件的方式,即对寄存器赋值,来控制SD主控制器,进而扮演SD主控制器的角色与SD卡取得通信。
) l# k$ ^: y3 Z( Z2 g/ x5 k& Y# O2 A" j+ D5 Y& _) Q7 J. Q, E
1) cmdat
6 V; w6 H) O" e1 m5 b  根据主控制器的芯片手册,寄存器MMC_CMDAT控制命令和数据的传输,具体内容如下,- E% f# y1 C/ k: V, |% ]
& j4 J0 t8 Q* q: F6 b
结合对寄存器MMC_CMDAT的描述,分析代码,
, a5 x! {% b+ v! r host->cmdat &= ~CMDAT_INIT;               // 非初始化状态
# O1 c& m' Q0 E0 C if (mrq->data) {                                       // 如果存在数据需要传输
$ v2 [: U& i( B# Y# I* s# g  pxamci_setup_data(host, mrq->data);  // 实现主控制器与SD卡之间数据的传输
7 [) P8 G1 U1 {# @/ Y4 |  cmdat &= ~CMDAT_BUSY;                      // 没有忙碌busy信号2 r9 q7 {! L/ f5 a  H8 ?. n/ t
  cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;   // 有数据传输,使用DMA
" b! y; }! L3 j2 n$ q" N  if (mrq->data->flags & MMC_DATA_WRITE)   
! N7 F# [- g2 i$ J   cmdat |= CMDAT_WRITE;                               // 设置为写数据
" m% {+ ^- G5 ]/ V$ k  if (mrq->data->flags & MMC_DATA_STREAM)
- ?+ F" p# f1 L6 X0 Y   cmdat |= CMDAT_STREAM;                             // 设置为数据流stream模式
, f! p6 _7 [, Z, I+ a# p1 y5 O* k}) u% }# Q. t0 a5 n9 }* _, ^  u
2) pxamci_setup_data  通过DMA实现主控制器与SD卡之间数据的传输( b5 l3 r9 B* F; I! l# V
static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
- j* I3 G1 T& A, h# _, o5 p* j  E{
1 E. M$ v1 P0 a6 j host->data = data;
& O: ^# o- q; w; E% R. s5 Z writel(data->blocks, host->base + MMC_NOB);     // 设置块的数量9 `! d# r* A6 Q9 h! I
writel(data->blksz, host->base + MMC_BLKLEN);  // 设置一个块的大小(一般为512byte), ~) w( l' d9 N& a- R1 `0 [" W3 ~
  if (data->flags & MMC_DATA_READ) {, |. o" P) k0 f7 A
  host->dma_dir = DMA_FROM_DEVICE;  o1 X2 A! V5 ?) |5 c
  dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;: q4 T0 f2 l: [* Q9 ?$ H
  DRCMR(host->dma_drcmrtx) = 0;$ e; [, H  f0 O6 d
  DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD;
  R' P  f% x6 c4 g& B } else {
; v( H/ Z# O* k6 N# E9 j3 O& S4 ^  host->dma_dir = DMA_TO_DEVICE;9 u7 Q" u7 J  W
  dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;9 r# o8 ?! @0 ?4 d% v1 ^
  DRCMR(host->dma_drcmrrx) = 0;
; U( Q0 @; Q) Q  DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD;
  k& r2 y$ Q! _5 W" O }
5 N8 g1 G6 c2 B; @$ i dcmd |= DCMD_BURST32 | DCMD_WIDTH1;% N: e  d1 W  f
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
( q$ `0 a  d% S. a( a' c  T5 ? for (i = 0; i < host->dma_len; i++) {
7 x7 K2 u# k# R" O  unsigned int length = sg_dma_len(&data->sg);
4 g$ F7 R+ E; W* r( U- \  host->sg_cpu.dcmd = dcmd | length;. z' l' F, R1 X2 i2 p3 X  k
  if (length & 31)
: d- b- X# p( Q; I   host->sg_cpu.dcmd |= DCMD_ENDIRQEN;$ ?; g2 O+ V/ ?4 V) W
  if (data->flags & MMC_DATA_READ) {
8 [( M4 K* h3 M( Z) w' V7 k   host->sg_cpu.dsadr = host->res->start + MMC_RXFIFO;
! W, T* C$ Y! q. R   host->sg_cpu.dtadr = sg_dma_address(&data->sg);! |' p% m0 M9 D7 j
  } else {
6 Q7 e& m1 J9 w* Z/ w   host->sg_cpu.dsadr = sg_dma_address(&data->sg);
$ X# e5 W7 L2 K. o( E4 K   host->sg_cpu.dtadr = host->res->start + MMC_TXFIFO;& g4 V# h8 b2 `
  }, e! C) t" G' W6 v" U5 i' _" i( R
  host->sg_cpu.ddadr = host->sg_dma + (i + 1) *
5 Z" q3 S0 A# c  D+ r. Q" @" i     sizeof(struct pxa_dma_desc);
. M" |# Y0 Q5 H3 q }
, O  u& k) J: K/ G host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
& H5 ~6 r$ a. R# c9 x  ]: v) {) { wmb();
3 [6 w2 v3 k( N" M1 W% Y. b DDADR(host->dma) = host->sg_dma;
* o/ ~: q9 H& p/ y7 s DCSR(host->dma) = DCSR_RUN;5 C9 X) {+ X) ]( D
}8 u" `, {# X) S# z( J  n
for()循环里的内容是整个SD卡主控制器设备驱动的实质,通过DMA的方式实现主控制器与SD卡之间数据的读写操作。# v* \3 P" h1 h' v& e: T! M: l
3) pxamci_start_cmd  实现主控制器与SD卡之间指令的传输
/ R8 W+ s! Z, F: L" ustatic void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)/ Y7 x% \2 F7 P1 D
{1 r; E' z3 m$ C# n! b* Y
host->cmd = cmd;" n: S  A( |% a" ~+ J; ~/ @5 D! ?
if (cmd->flags & MMC_RSP_BUSY)- W4 I# t  B6 H- s* r1 K8 k
  cmdat |= CMDAT_BUSY;$ Y. H& {' \# _  _& i5 G: k
#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
6 z6 e& w$ }: l& _: H" x; E switch (RSP_TYPE(mmc_resp_type(cmd))) {- s1 n  O3 D( V2 T2 P5 J
case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */- {9 Y3 q' Q2 `7 ^: \+ e
  cmdat |= CMDAT_RESP_SHORT;
2 Q: z3 _3 m0 z/ r  break;
8 F& A( ^7 H5 t: j- K+ i case RSP_TYPE(MMC_RSP_R3):
: Y1 f8 G' p4 C" p( J! Z  cmdat |= CMDAT_RESP_R3;* V& Z' c1 }; J' c0 s
  break;9 z' i( ?% V1 R- k. p2 W
case RSP_TYPE(MMC_RSP_R2):
5 p0 T4 H* _# F) y5 U( `! l  cmdat |= CMDAT_RESP_R2;4 ~" W0 x6 D- h: x  l& Q. ^+ P) F
  break;
- Z) s3 V$ W6 v default:2 r/ a" X4 x  m4 _3 P3 A4 k
  break;; Y* @" R/ U2 u9 R
}
7 @" Q2 Y' k  E writel(cmd->opcode, host->base + MMC_CMD);
3 ?' r2 Z- E! k" A1 s1 w0 A writel(cmd->arg >> 16, host->base + MMC_ARGH);
  h, {8 X  S) o' r7 n$ o3 ~ writel(cmd->arg & 0xffff, host->base + MMC_ARGL);& ~/ ^' P5 [( H+ O* @0 o0 O+ p# M  r2 Y
writel(cmdat, host->base + MMC_CMDAT);! I* G' B: g( e1 I7 Y2 z
pxamci_start_clock(host);
+ I" K; O1 e. k* R7 t, B pxamci_enable_irq(host, END_CMD_RES);5 |; h% b# b  n
}$ r" @: q& M3 z5 P; h/ ^9 F( S, O
-> response类型
$ x0 [6 s$ m6 J+ R/ y4 d  根据SD卡的协议,当SD卡收到从控制器发来的cmd指令后,SD卡会发出response相应,而response的类型分为 R1,R1b,R2,R3,R6,R7,这些类型分别对应不同的指令,各自的数据包结构也不同(具体内容参考SD卡协议)。这里,通过RSP_TYPE对 指令cmd的opcode的解析得到相对应的reponse类型,再通过swich赋给寄存器MMC_CMDAT对应的[1:0]位。
% @2 L+ `- |. ?) N# K# @-> 将指令和参数写入寄存器8 {! B* V% d) m8 ~/ c# x. R* o
  4行writel()是整个SD卡主控制器设备驱动的实质,通过对主控制器芯片寄存器MMC_CMD,MMC_ARGH,MMC_ARGL,MMC_CMDAT的设置,实现主控制器发送指令到SD卡的功能。0 C' E, g+ l9 v0 y# H0 p+ [2 R& G
4) 调用(*request)
3 h: G! U& b- e 以上通过pxamci_request()实现了主控制器的通信功能,之后只须通过host->ops->request(host, mrq);回调函数即可。
; }) F6 G5 P) f+ n协议层里,每条指令都会通过mmc_wait_for_req(host, &mrq)调用到host->ops->request(host, mrq)来发送指令和数据。
5 p6 e" d' C0 B* y9 L) {! _! E; c/* core/core.c */; j0 Z' s; j  P: s+ A( C
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
1 P# N* ^+ L5 ^, R8 t& q3 J9 N* w% }{
: K& W* v7 Y9 T" j9 Z. `4 D2 ^ DECLARE_COMPLETION_ONSTACK(complete);
; e" A2 j, C. H) M: W mrq->done_data = &complete;; t9 @7 v5 q0 b  ]% r
mrq->done = mmc_wait_done;# s1 v  L  k( B2 z; }
mmc_start_request(host, mrq);$ v/ t  T- Y/ M, u2 K8 e* @
wait_for_completion(&complete);
. r- s! V; R7 J9 @" Z}
3 M5 k6 ~8 R/ F: M' [, t/ ammc_start_request(struct mmc_host *host, struct mmc_request *mrq)
$ ~  `% b( x6 J( V{
4 Q  N* H" m8 Z8 Z2 }+ U# I+ ?/ u- ^) G ... ...
) e7 T! L# ]4 Z9 P* p" \ host->ops->request(host, mrq);
. L  o& E8 j% `: Q}

6 K: k0 K2 M6 Z4 l6 x" R6 H' T" n( Z! L0 n& Q) j, W' t
2.2.4 申请中断

5 g+ z: {6 b) @) e! W4 `  pxamci_probe(struct platform_device *pdev)中有两个中断,一个为SD主控制器芯片内电路固有的内部中断,另一个为探测引脚的探测到外部有SD卡插拔引起的中断。' e$ E+ ~) ]7 W. t
1) 由主控芯片内部电路引起的中断( d: A9 N, s/ ?0 u3 ?4 }
request_irq(host->irq,pxamci_irq, 0, "pxa2xx-mci", host);9 _) B4 b4 ~3 h% f  }/ E, W$ F
回顾一下,host->irq就是刚才从platform_device里得到的中断号,2 D" Z* H& [/ j  U/ x: w- E6 B
irq = platform_get_irq(pdev, 0);
! L& q1 D. n- m! |0 u/ |. Y host->irq = irq;
; w: u( K5 ~- q$ H; M接下来,pxamci_irq便是为该中断host->irq申请的中断函数,' M! l# X( N, e2 w3 R1 V5 \( O4 w
static irqreturn_t pxamci_irq(int irq, void *devid)
0 w  [( g- [. a4 |{7 R) ^" m0 ]' o8 o% J# [6 ^
struct pxamci_host *host = devid;   // 得到主控制器的数据
2 I- I! i: X9 R$ H unsigned int ireg; int handled = 0;; I8 R% @, t2 V, ]& }. U. H
ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK);  // 读取中断寄存器的值
1 Z8 L' n/ L1 ^! m, t) o$ a if (ireg) {4 U& G3 n& p9 L/ M" \( D3 v
  unsigned stat = readl(host->base + MMC_STAT);  // 读取状态寄存器的值- c# F2 A8 u6 y# q; M
  if (ireg & END_CMD_RES)& v* w& f7 v$ |( H6 S
   handled |= pxamci_cmd_done(host, stat);! F( p0 P/ i4 @. s0 E/ H7 y
  if (ireg & DATA_TRAN_DONE)7 M( q0 B7 ]" U, S) |. Y* c
   handled |= pxamci_data_done(host, stat);. L4 T! t2 r. Q1 ?2 o
  if (ireg & SDIO_INT) {
4 u6 v- X) P7 E8 z   mmc_signal_sdio_irq(host->mmc);) h( u6 N* y0 {! f. T: o7 B' y/ q
  }
7 p; {" I3 R& B) _ }7 q$ Z$ w" q3 T8 U8 N- u
return IRQ_RETVAL(handled);4 q4 l* W" w% E0 O; `+ ^( p' I
}
4 E; S. B1 I" O  d+ \, S当 调用(*request),即host->ops->request(host, mrq),即上文中的pxamci_request()后,控制器与SD卡之间开始进行一次指令或数据传输,通信完毕后,主控芯片将产生一个内部中断,以 告知此次指令或数据传输完毕。这个中断的具体值将保存在一个名为MMC_I_REG的中断寄存器中以供读取,中断寄存器MMC_I_REG中相关描述如下,
8 N6 I5 y& F) N# V7 m$ Z8 f& t* U4 R' \) G4 e5 S8 w
如果中断寄存器MMC_I_REG中的第0位有值,则意味着数据传输完成,执行pxamci_cmd_done(host, stat);* _) z( d, m# ~4 k+ w' l' R& T+ m0 b9 ]  z
如果中断寄存器MMC_I_REG中的第2位有值,则意味着指令传输完成,执行pxamci_data_done(host, stat);- N* H1 L& i( P7 |+ H1 H
其中stat是从状态寄存器MMC_STAT中读取的值,在代码里主要起到处理错误状态的作用。& u' e7 {0 A8 y6 z
-> pxamci_cmd_done收到结束指令的内部中断信号,主控制器从SD卡那里得到response,结束这次指令传输0 I: o( ]9 x3 ]
这里需要注意,寄存器MMC_RES里已经存放了来自SD卡发送过来的response,以供读取。  I" w- f1 D3 D  m" \8 z
static int pxamci_cmd_done(struct pxamci_host *host, unsigned intstat)
- F' Z! I: r! L7 Q  y% c{/ |' {7 {4 {: Y& [/ n, G
struct mmc_command *cmd= host->cmd;- G. B. R7 o5 }" a. [- M
cmd->resp= readl(host->base + MMC_RES) & 0xffff;
8 O4 D( V: }" \ if (stat & STAT_TIME_OUT_RESPONSE) {
' T2 e" x5 C( c: j/ _( ?  cmd->error = -ETIMEDOUT;
6 C' T2 X6 P: T9 l5 P! w9 e; t } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
% O1 g6 l6 M8 f! m! I/ J7 C   cmd->error = -EILSEQ;( a+ |# a  b4 ~
}
7 l; ~7 G' I2 ~7 c# D2 C8 h! f& [ pxamci_disable_irq(host, END_CMD_RES);
) B5 Y- t  o& \' t2 o if (host->data && !cmd->error) {
, `& N, T  V8 _9 O  pxamci_enable_irq(host, DATA_TRAN_DONE);
( c$ z% j+ t7 D6 t+ Y7 j } else {" {: S7 [" H5 V. z6 ~+ j- ]
  pxamci_finish_request(host, host->mrq); // 结束一次传输,清空主控制器中的指令和数据
. w2 ^5 L( ~1 `5 I }
( m1 D" n! H; Q8 b, b$ n return 1;
  g1 ~1 v/ c) a}) s6 F- |! R7 a& K  G. r. o2 _
-> pxamci_data_done 收到结束数据的内部中断信号,得到传输数据的大小,结束这次数据传输
4 Y0 C2 y5 B: s  B  D" V8 D. q static int pxamci_data_done(struct pxamci_host *host, unsigned intstat)! d# Y- O6 E5 {
{% Y: K. j& y" l4 C
struct mmc_data *data = host->data;
  i# m5 I1 ?) X* v4 M8 O DCSR(host->dma) = 0;! \) o" z, P+ b2 S  {1 n
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, host->dma_dir);
) j5 d* i) h& N- n  Z: Z if (stat & STAT_READ_TIME_OUT)) H& a/ ]' a0 B. e, j
  data->error = -ETIMEDOUT;
+ W" C  g3 z8 x3 J else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))+ ]! R% \7 {) \2 r# o
  data->error = -EILSEQ;! `/ E6 J. A$ U2 G9 {, z+ V
if (!data->error)$ \9 C$ L4 G" _- q9 q( ]1 w
  data->bytes_xfered = data->blocks * data->blksz;  // 数据传输量=块的数量*每个块的大小(一般为512字节)
) b4 M' d' X# [! L7 T else) Y* q/ q: c8 {! {1 }
  data->bytes_xfered = 0;
0 n) o7 [0 O! `9 F$ n# ?" [4 f3 y pxamci_disable_irq(host, DATA_TRAN_DONE);
2 H; f& d1 s/ V3 R host->data = NULL;
- w3 {" l* N+ r7 g! ^; d pxamci_finish_request(host, host->mrq);& i* Y" N$ ~6 V# c- Y# ?
... ...
- e9 X& ^! h7 F" T}8 r( P3 W4 Y, v6 T5 r1 K7 m
static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq), s1 ^' @; P- r
{& e- i4 r/ ^+ n  u" K' c; x$ x
host->mrq = NULL;
' B( ?8 T0 y5 @; d host->cmd = NULL;9 Y3 q) n) G  ]* o' |" u
host->data = NULL;
: t9 t& `: b4 V$ ^ mmc_request_done(host->mmc, mrq);
$ f" S& y" x* P! ?  @; m+ M& O  H set_mmc_cken(host, 0);
/ R$ a: ?, @0 Q- l; k unset_dvfm_constraint();; M9 v; \8 \- n2 |8 R; I
}
8 O+ d6 h3 U3 Z3 }' @+ X5 }/* drivers/mmc/core/core.c */  r2 |: f) v8 m( c3 l5 u8 `  _
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
# Q& }$ j& P$ O) [* H1 l{: z4 _7 O8 |5 I+ J
   ... ...
" G' d% _6 Y" X. g/ U   if (mrq->done)
* P6 f5 K0 W) E: O' F7 `   mrq->done(mrq);
) y# x9 x6 d; Z' A} ' O! j, C$ `, V4 D/ e3 Q4 [# [
这里用到了完成量completion,LDD3上是这样说的,完 成量是一个任务的轻量级机制,允许一个线程告知另一个线程工作已经完成。这里的一个线程就是内部中断处理函数,它是结束主控制器与SD卡间通信的线程,通 过mrq->done(mrq); 即complete(mrq->done_data);告知另一个线程-回调(*request)实现主控制器与SD卡进行通信的线程,通信已经完 毕。' r; t3 x& [, r! U% z0 ~
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
$ p2 y$ y, G2 F& g3 s{3 U  d3 `% l: ^& n+ X! s% I0 h1 ]
DECLARE_COMPLETION_ONSTACK(complete);
0 q$ Z1 f; C  f mrq->done_data = &complete;
' Z; j: W( s$ m4 o8 ~ mrq->done = mmc_wait_done;( m3 D  p: l, y( l  l
mmc_start_request(host, mrq);
7 x0 P  z5 x; Z wait_for_completion(&complete);
0 ~+ `7 _1 V7 N2 y2 M9 J}- X: s) e) ^/ a4 D3 t
static void mmc_wait_done(struct mmc_request *mrq)# K! _! i* t/ g* ?0 ~( ]
{( `  j- E% |. O! |5 t  \; s
complete(mrq->done_data);
! R$ o% P4 ^9 @# K$ d4 X}, d- j$ {) V) y8 r( [) h
2) 探测引脚引起的中断! t5 Q. Q* e  |2 r' Q3 B
( R: G* ]6 U8 D9 g/ \8 a0 |9 B) n
  if (host->pdata && host->pdata->init). K  C8 {' r* x, V/ M7 ^
   host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);! y5 u1 c! z8 U( l! r6 J7 U9 d
该init()回调函数就是刚才提到的系统初始化时通过saar_mci_init()实现的, 0 {) i: D. p* V. l
static intsaar_mci_init(struct device *dev, irq_handler_tsaar_detect_int, void *data)- v! F# z" O1 _: Z. [: R; Z
{
5 h! r3 n1 M9 M1 o6 i! o6 p request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "MMC card detect", data);
' T' c8 Z% M! A7 K% d  S# M ... ...
! y# ]6 v% l8 D0 B+ S4 S! z}+ o* [3 v# n- z5 N* E. B
其中,cd_irq是通过GPIO转换得到的中断号,pxamci_detect_irq便是该中断实现的函数,之前已经提到过mmc_detect_change,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数。" {  u8 n, I6 z/ v
static irqreturn_t pxamci_detect_irq(int irq, void *devid)  J" E0 f1 @1 D3 T5 t$ I
{$ ], t! t4 d+ S( _
struct pxamci_host *host = mmc_priv(devid);, i  s  ^7 v( e# r  D3 W+ U
mmc_detect_change(devid, host->pdata->detect_delay);
0 W2 O  j  d0 f& V# G" \9 D return IRQ_HANDLED;% I) ^& `2 s" s# c( j
}1 U. f/ S" [5 x5 ]' C$ h
当有SD卡插入或拔出时,硬件主控制器芯片的探测pin脚产生外部中断,进入中断处理函数,执行工作队列里的mmc_rescan,扫描SD总线,对插入或拔出SD卡作相应的处理。下文协议层将讨论mmc_rescan()。
; i9 w% `) s6 j
9 _3 R' i' I- r6 v
  3. 协议层/ j3 h" Y9 y( ]( T7 G) q. I  S
+ _: Y2 u8 \* U( Z
   Linux在内核源码的drivers/mmc/core文件夹下为我们的提供了一系列SD卡的接口服务函数。可以查看Makefile如下,
/ a' A# x8 t- F9 e! c( d
5 S( Q. _+ G3 {2 ^  X" G可见,core文件夹下有针对总线的服务bus.c,针对主控制器的服务host.c,针对SD卡的服务sd.c, sd_ops.c等等。
9 V0 ?9 |1 T1 f& A, `; |9 O- {& Y
其中,最为核心的一个函数便是之前提到的位于core.c的mmc_rescan,概括来讲,主要完成两项任务,即1 a1 d& N! L4 O$ V

' ~1 ^. _5 |& S

9 W* Q1 z3 E5 M5 w0 |. v9 l+ ~+ N: J: C% U) `2 ?
3.1 插入SD卡: C5 U1 c% ~# d* E

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

% P5 ]( k5 {/ {% C( i+ k2 w  ... ...
2 U+ z1 m$ {8 M3 e
$ z$ q6 x! N6 O9 Y6 R' b9 ^}
" f* N3 k6 P2 G9 `' a1 Q5 p! n7 f6 A3 m+ e0 i
插入SD卡,mmc_rescan扫描SD总线上是否存在SD卡,具体的实现方法就是通过向SD 卡上电,看是否能成功,以普通SD卡为例,为普通SD卡上电的函数mmc_send_app_op_cond(host, 0, &ocr);如果上电成功,则返回0,即执行if()里的mmc_attach_sd()进行总线与SD卡的绑定。如果上电失败,则返回非0值, 跳过if(),尝试其他上电的方法。那么,上电方法究竟有何不同呢?具体看看mmc_send_app_op_cond()的实现过程,
6 Y, [* G, }. f1 v1 ?6 N$ I! B" L
2 X- c7 B" F# P3 K2 }  dint mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
( c! \! [4 h% G+ H" [{
0 W4 Y$ y7 V" A& Z8 [8 z struct mmc_command cmd;
* b* E5 b3 J2 F5 h! W cmd.opcode = SD_APP_OP_COND;    /* #define SD_APP_OP_COND   41   */2 g* g% |  m; f1 U! w! v  N
mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
; P* `+ ]$ l& P7 l7 U! T( w3 x" d4 [
... ...& X# U4 r4 C2 N( l/ V# c

3 P& }7 A3 Y0 N1 x- q3 J( P  a1 j}
' l6 E& _6 r; t, Wint mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries)
& ~( t0 ]3 g- W1 W6 S; S/ n{4 I/ e3 q1 p5 n. Y7 n( T

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

) ^" t! l; v! e( l
( Z' P4 E3 b! g* K1 l$ ^static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)2 f6 G) @) V) G# v8 C
{0 t& `. r; K5 L- {- _8 D
, W- Y& a2 h8 j% F. q1 y5 ~. @. b  B+ f
  /* SD卡的启动过程 */
* ^  {( X4 m% ^2 r
) F6 D5 i# R; C( K. H" T% O  mmc_go_idle(host);+ x4 }, T9 L+ m8 b0 N2 {$ s5 {

* q6 |+ p& m# `1 Q. D6 C  mmc_send_if_cond(host, ocr);
& x$ D" S+ v  b" T. s7 P) m  @. z/ Z# c# ]$ `3 k1 R
  mmc_send_app_op_cond(host, ocr, NULL);; ^6 X8 c1 p" N3 ?1 `0 \
/ S6 e- d4 ^/ u  i7 ~" `6 A/ R4 }, q
  mmc_all_send_cid(host, cid);
0 a/ b+ k. S  {( X
2 _4 e5 J, f- Z, H3 b( @  mmc_send_relative_addr(host, &card->rca);
; x; ?) O: r' z- B7 h( {0 s5 T/ x/ G$ d9 i% J, ~( ]6 o* W
  /* 得到寄存器CID, CSD, SCR的数据 */1 E- a& I$ Q2 m5 v5 M
2 S* I/ j9 ~" L, ^' t. N
  mmc_send_csd(card, card->raw_csd);4 V: F+ a# m) r7 V  a/ A7 F  w8 `. p
" }6 z1 d! v+ d7 \& ]
  mmc_decode_csd(card);6 W" k2 |' K3 @6 s& w

4 q. Z+ Q4 y9 }8 K" Z& ]# V  mmc_decode_cid(card);
9 c4 e5 ?7 p+ ^) f9 L. {5 e. H* j. ], D# f  X1 E6 v! x" V
  mmc_app_send_scr(card, card->raw_scr);
$ y8 T: F0 c' E9 m* U! X3 t+ i) S6 `& d3 a+ G$ h) u
  mmc_decode_scr(card);
- s! [9 q3 _/ C$ P* m; z! T9 k% {5 B! h# A
  /* 其它操作 */
% Z4 a. k/ D" ~5 X+ r7 ?/ O5 g) A8 v: G* P' A0 y/ Y% [( U! O
  mmc_alloc_card(host, &sd_type);
& G, `/ z; ?* }# c: C8 s2 h6 i6 K% V% V
  mmc_select_card(card);
& E0 I* w- p- E; j# ^3 @; P
7 P. c% D7 X5 Y. |# F4 ^* t  mmc_read_switch(card);4 q, e  C, N$ C* C, c
; a3 n( J! j& ?: K& r  O
  mmc_switch_hs(card);2 @/ y  j2 l7 f  n5 Q

! ?' ~. o' D" z8 m3 \2 f# M; v% n  ... ...
! h4 A: v, b. M+ `  V  V2 y0 S+ L2 D, ~& S. b0 {! o
}: ^! n- E: @1 X+ n+ N  l8 F
. s2 p! e. I# t  O$ |3 u9 d& I8 F7 w
1) SD卡的启动过程
8 }0 b9 j, c6 Q1 ]' i& E7 Q* I5 G* L9 e8 E/ N3 p1 V8 m: A
  根据SD2.0协议,SD卡的状态可分为两种模式:卡识别模式(card-identification mode)和数据传输模式(data-transfer mode)。这里,我们关注启动SD卡的卡识别模式。
+ j6 K2 f1 E) |4 s6 R. H9 {% N. r; _4 W' J) p

! @: [3 ^0 m- ]0 n* j! {8 j0 x, h0 Y- S$ [
结合代码,
, s! B3 B# Z. D: [( T
8 Z9 ?5 @. Y/ P8 W" c' l& X  mmc_go_idle(host);                     CMD0' v3 u9 g/ @$ G8 T8 D" b

$ v( H/ p3 n4 @- b; J! ?& S$ |2 Q  Idle State8 {7 J8 w1 \" j" h* [3 V& B
( M8 B9 G1 p; r, E2 |
  mmc_send_if_cond(host, ocr);     CMD8
1 O+ M7 [- Z9 K9 `) [( Y6 A; x. F: _0 g
  mmc_send_app_op_cond(host, ocr, NULL);       ACMD41" N4 W/ p3 F, |  i4 S" T

) @1 ~- u. a! L  Ready State
  @: }" U+ y  Y6 U
( H. j$ O( z5 ~/ k  mmc_all_send_cid(host, cid);       CMD2
& c  Z5 I( {. ]1 R+ i' `. W/ i/ M  B5 z2 \9 ?
  IdentificationState
! \3 e' q9 A% s& A" X& Q0 {
3 z" {) H( r- Q" @  mmc_send_relative_addr(host, &card->rca);     CMD3
5 p) g# p% p3 N1 G  u8 g3 b
  X" j1 H, p6 Y6 S  Stand-byState2 P$ o; T; n( `8 H

7 Z6 q% z4 \/ s6 U7 V( j$ k- O$ X2) 寄存器CID, CSD, SCR, RCA
) {/ f/ e" {% D, P; P. Q1 G2 ~! E9 [  L( p3 s0 i" H& u3 r
-> 发送指令并得到寄存器的值# _. N, m" g$ h

- v: o0 ]# g. d1 w. @& T; H: O0 n  当主控制器向SD卡发送cmd指令,比如mmc_send_cid(card, card->raw_cid),请求得到SD卡CID寄存器的值,当主控制器发送cmd完成后,芯片产生一个内部中断,处理结束 cmd的中断函数,之后得到来自SD卡的response,即CID寄存器的值,存放于host->cmd->resp中。关于内部中 断处理,参看上文的中断一节里的 mmc_wait_for_cmd()。3 D$ ~, @% i6 H' ^. y2 C3 v

, ~5 Q$ ?" ?2 {& p4 M) A0 A. y- cmmc_send_cid(card, card->raw_cid);这个函数发送了接收CSD寄存器的请求,并且得到了来自SD卡的CSD寄存器的值。
# o/ _: C9 w7 |+ r7 q( ]. c% t3 _2 f$ E
int mmc_send_cid(struct mmc_card *card, u32 *cid)1 P  h) q# o( v" N
{# d% b) a6 ]% @% p
   return mmc_send_cxd_native(card->host, card->rca << 16, cid, MMC_SEND_CID);7 R) q+ |7 }* k, t6 B
, {7 q6 l. c8 W; `( W* v
}/ H  W6 M! T) i6 v

- }- z' C4 H3 o/ b( m4 t4 ?. cstatic int mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, intopcode)
* z8 Y0 Y$ S3 \9 ~{- j$ F5 f  X4 J' L  [+ O& S
cmd.opcode = opcode;
! u( c% c5 e$ A5 w0 U2 T cmd.arg = arg;* a3 _* E- a+ K" B# o$ J
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
" x6 I! P- T1 B$ L; B  h7 J1 J5 V* O( c3 h! |
mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);6 ?+ ?% u/ @* X( q9 ~- g
/ U" a5 k+ H6 {( u( x- j" `
memcpy(cxd, cmd.resp, sizeof(u32) * 4);  // 得到response赋给cxd,即card->raw_cid
: z0 |* p) n4 X, r& s$ j8 H: `. |& q1 b+ K+ S
... ...
  c  v+ R: V# k! w) J7 \  P}
9 {+ M6 \9 R8 A# a+ M/ |! I# J6 p
: z, K" w0 b. V8 ~. b% E. [-> 解析寄存器的值
4 W. z1 u! Y( t+ p# k
  q2 [- }9 M' C. J% z为什么要解析?先来看看寄存器CID在SD卡协议里的定义,它是一个128位的寄存器,存放了关 于这块SD卡的基本信息,就像自己的身份证。通过mmc_send_cid()将这个寄存器的数值赋给了card->raw_cid (定义 u32 raw_cid[4];) ,为了方便得到具体某一个信息,协议层为我们解析了寄存器里的域,并赋给card->cid,比如厂商名称,就 可以通过card->cid.manfid直接读取到。6 g1 A5 s4 ~  c9 W' L( _% m

# K" ]5 `8 |2 s4 k  a( W$ u# Y. p

" I! h! k) i. d) E, D9 t6 v2 }static int mmc_decode_cid(struct mmc_card *card). T1 l8 K' m  l, w
{
7 K% D: a: M; M  Z+ u1 G0 t  u32 *resp = card->raw_cid;/ I! H% D6 n* i2 f9 r+ D2 b# u  u
  f! x# b/ W& M" }) y$ V& j
  card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
: s  P  y) r" a- U  card->cid.oemid  = UNSTUFF_BITS(resp, 104, 16);
: n2 p( |0 P- o( I, q1 S1 G- e  card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
6 }8 o, ?5 C% [0 O7 ]' G: S2 s- E  card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
8 Y' |9 f9 a+ G3 O  }0 t5 ^  card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);- X% a2 }0 ~) E* R) L1 I4 s
  card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
) O' Q9 V5 l% C% `' n1 z/ f  card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
! s/ ?: @% E+ }- |  card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);4 X' c, d" T: V& D4 l( _
  card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
7 q4 c( r3 R, p; i) t. r  card->cid.month  = UNSTUFF_BITS(resp, 12, 4);
! @( c/ t) D) c, S6 o8 @7 w5 n" ^( U  card->cid.year  = UNSTUFF_BITS(resp, 8, 4) + 1997;) v9 a8 H% I$ i: \7 g! a
  return 0;
0 n& [* [" ~$ K* t7 d( o}
8 }( W7 ?# y1 s
6 C; `9 @+ |9 Y5 L' ]/ }3.1.4 注册SD卡设备驱动3 `8 O( ]; r* K, }& M2 ?; `
4 D0 n7 ~. A6 z# D& {! H1 V
int mmc_attach_sd(struct mmc_host *host, u32 ocr)6 x2 y1 S; v2 w/ G
{. j  {$ S7 O$ t: l% k+ h4 e' L* ~

' R! }( F6 j' y: x( A  /* mmc_alloc_card(host, &sd_type); 在mmc_sd_init_card()已完成 */) [4 \$ }! k+ _
5 R0 {3 ?# S( Y+ Q- w- f
  mmc_add_card(host->card);6 Q7 p* f: P; l3 p3 S7 _- H
6 K- V: n6 A+ }/ ?& M; M
  ... ...( Z1 Q( T2 I2 s- n! ^$ a6 I/ M
# Y* l4 ?& q/ U3 U4 [$ O/ k
}
" J  A( H, B/ c7 `. c
, Q6 C1 k. x) }; D上文已经提到,设备驱动程序都会通过alloc_xxx()和add_xxx()两步来注册驱 动,其实质是调用/drivers/base/core.c里的device_initialize()和 device_add(),device_add()完成建立kobject,sys文件,发送uevent,等工作。
$ N) E( s7 M- U) v8 L9 O  Q4 v; B; k1 R, W5 P' R. c' `. T- t+ b
3.2 拔出SD卡: d% M# X4 }7 k) N: e6 B  N" u$ f$ u

; E: T' g4 l- w- K. `2 h0 s  N- z void mmc_rescan(struct work_struct *work): U. o0 W& l. ~" @! C
{$ }' P* W0 z7 v) L- ]: Y, W! v' R  ^
struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
9 x& T0 g& F0 U! z7 f) | mmc_bus_get(host);
% Q3 C5 H# {- }) c: J* i4 V5 c- v- A/ |4 u& Z8 w
/* if there is a card registered, check whether it is still present */
; P) s! g( k6 R* X; [ if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
5 {% D& ^) v" a* C/ U6 Z# h0 W  host->bus_ops->detect(host);
5 a+ u, ~: E2 \5 V$ S
* R1 V" Q. c) W/ C mmc_bus_put(host);
0 b$ R6 a' Q2 V
9 l' u6 b* t; W9 ?- |+ | ... ...# f' x/ K( P6 {8 d4 a0 Y

2 K% f% m" a" p" _! U# M8 ^. T}
6 ], y8 x1 q7 T: p; v) g) v! _( D! `$ Z: j/ l+ D
这里的mmc_bus_get/put(),为SD总线加上一个自旋锁,规定同时只能有一个线程在SD总线上操作。
8 U! w2 N+ s0 x0 x+ q$ _  g4 e) ?8 [8 K
3.2.1 bus_ops->detect()( c: L$ P& U7 D2 L

6 Y" V) W& J2 qmmc_rescan()扫描SD总线,如果发现host->ops上赋了值,即之前已有 SD卡注册过,就执行bus_ops->detect()操作去探测SD总线上是否还存在SD卡,如果不存在了,就执行 bus_ops->remove()拔出SD卡。之前已经提到,这个bus_ops->detect()已在mmc_attach_sd() 注册完成了。8 b# Z+ X* V3 w: Q) _0 A* V
5 `1 d$ |& y1 P0 P. `: J
static void mmc_sd_detect(struct mmc_host *host)# v" j6 ^/ V1 j: k
{+ ]# e/ j; r+ r" r
mmc_claim_host(host);
6 {5 h2 B: y# N$ k9 R
: [. f) I% b5 N* g6 m /*: Q( Y/ M4 n5 s, S! ]7 j
  * Just check if our card has been removed.. d4 p+ u6 w" ^' h
  */8 \6 B4 |, {2 O0 R2 a
err = mmc_send_status(host->card, NULL);
1 o, P7 @/ A! D1 |; L- R) Y1 Y- c3 }3 V4 ~+ m
mmc_release_host(host);
+ J' k2 K1 Q6 x$ X2 i# C$ X3 j2 d+ L$ m4 k5 r. |: H% S
if (err) {
: W0 F9 f- O; {# o! i$ K  mmc_sd_remove(host);
5 K1 f( j2 I# c7 ]  T; w. ?$ M! V; g! y7 O( C: _( k
  mmc_claim_host(host);
* _! J7 }$ ~, @! ]: F! @; A# e  mmc_detach_bus(host);
; p# P! A- p, P0 ]7 |: ]  mmc_release_host(host);
6 z: K  ~' n* L9 B4 ~ }: C6 W6 W, _1 J' D1 K
}' y5 h8 b$ |2 C* k1 r( T
这里的mmc_claim_host(host)通过set_current_state(TASK_RUNNING);将当前进程设置为正在运行进程。
, z( F8 {" l; q5 [
" ~6 \. k9 c. Hmmc_send_status()发送得到SD卡状态的请求,如果未能得到状态数据,则执行mmc_sd_remove(host)拔出SD卡。
2 g' [' p, ~% j* _4 B
2 C! B9 t9 @  [! v7 U( n8 S5 h4 Bint mmc_send_status(struct mmc_card *card, u32 *status)
0 O% a, m  U/ S1 e$ W5 L% q{
1 W0 v8 S% j, q' O$ b struct mmc_command cmd;
  d- }% y" `  O$ v& ]% S
! F* ^+ h' H# G$ ~ cmd.opcode = MMC_SEND_STATUS;    /* #define MMC_SEND_STATUS   13 */
! @' |) q+ b! h  N; {5 y3 Z cmd.arg = card->rca << 16;/ f% L9 X( a. {& ~# l, j1 p- Q9 |
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
$ {1 o2 ^; L  O. @6 e- G; S
% C) E  V2 O4 m* Q6 ], r, n% ` err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);# U& b4 C6 t: i# W4 [7 R2 ]. T$ b
& N' ]4 m5 a: f& W$ e( G6 U
if (err)               
/ e" d% `2 S- u$ C/ R  return err;           // 接收来自SD卡的response失败,即没有发现SD卡& u$ @" B/ G5 f3 A0 [
if (status). @* y8 [: Y4 Y' c5 l! V1 I2 E$ s
  *status = cmd.resp[0];( _- s6 [, R$ U: I0 e3 t7 {6 Z
+ ^5 o) O4 u; q! z  X, h$ O
return 0;( K5 h8 D3 T4 t

( E* P" o  v$ R( \3 B, C}* B: v( {  s6 D% ^
主控制器发送命令CMD13,要求得到SD卡的状态寄存器CSR和SSR。  C: h8 I2 C% L0 Z9 C
4 H; A( H% ]2 j5 S; K0 K
SD协议规定,状态寄存器CSR是必须的,这个32位寄存器作为R1的一个域返回给主控制器,
  L) [5 M6 @2 V  J2 G/ n8 i3 B( w; M8 @
状态寄存器SSR作为扩充功能,具体参考SD2.0协议。( ~" D# K" }, y" i9 {

; 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
* 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

+ 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

/ D- A6 v1 _# U- w我们不必深究所谓的电梯算法,只要知道,它是使数据得以高效通信的一种算法,算法自身决定何时去唤醒守护进程处理请求。0 V' k4 V6 l9 x8 J

* d( ?! `  Q) f" l2) blk_init_queue()
$ M9 W# g: C1 @5 }( g" W; d4 X$ V, P" N2 b8 m
如果一个块设备希望使用一个标准的请求处理步骤,那就必须使用blk_init_queue()。这个函数注册了q->request_fn(这里就是mmc_request),并初始化请求队列的数据结构struct request_queue。
1 f/ O3 Y6 N* h9 y+ v2 u7 Q) e2 U; P/ ^  A. m  J
/*
# _# `1 \$ `5 g* R7 h
' p, Z; r* D3 X% z! J *    call blk_init_queue().  The function @rfn will be called when there5 a2 \; p# f5 l) d# a
*    are requests on the queue that need to be processed.  If the device9 ^6 Y5 M$ D5 j% D8 K
*    supports plugging, then @rfn may not be called immediately when requests
7 }! u' ~. _8 e; n( j2 S9 N *    are available on the queue, but may be called at some time later instead.
& Y% G; a4 \' W+ M, U; c */ & m7 j9 y2 X: I2 q% u
" P& B' B$ g0 U$ o% o+ [
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)% L0 [: i5 X; V9 M
{- U; M0 S$ G' [2 r' p+ Z% s# N
return blk_init_queue_node(rfn, lock, -1);0 z% m* p/ \* Q% K! k+ u0 [/ E
}* t& o0 f3 J$ v- b7 j& m% B
0 ~% c4 F6 @( A* p0 n2 P# i7 U
其中的rfn就是请求队列的一个算法,即这里的mmc_request。3 A3 z1 S2 G* {7 W0 b3 ^) ]
6 a' |* t' v8 |$ G2 ]
struct request_queue *blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
5 i9 u; y7 P" A; \0 O1 M7 [  p* M{
/ B3 e* G- f. y3 W+ p struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);
2 L' A: B9 w( w
5 R2 b9 f' C- @: r0 f9 U q->request_fn  = rfn;7 b0 s# t8 b3 x$ x/ d, l
q->prep_rq_fn  = NULL;5 O1 l3 M1 x9 @4 Z- N/ a- [
q->unplug_fn  = generic_unplug_device;
7 m5 K/ m8 x% A1 B9 h3 ~( ~1 J4 M# a: d. q q->queue_flags  = QUEUE_FLAG_DEFAULT;
+ P* M  j4 ^8 C/ W" v# F3 F! m4 x q->queue_lock  = lock;0 e/ ]. W  G( }4 T" \& u5 O+ l
9 o. y1 N* O+ H& _3 \, Q
blk_queue_make_request(q, __make_request);: T1 R* ]% L6 \) a  i+ S
" `2 H( d) p& C/ R1 `9 m
if (!elevator_init(q, NULL)) {
" F& W! [: P. ^- g/ P; Y0 u  blk_queue_congestion_threshold(q);1 z9 V0 q4 `0 ]% M
  return q;2 c( C3 x5 p6 z4 Y+ T% ?1 {8 G
}  A+ J: }( N1 h) R* x: _$ k$ C- }
" l5 x, f8 a; G: n* q
  ... ...0 A4 `$ g1 c  }, B: m) @: k

6 R7 a  d/ c* x: D; W. O- P5 j/ ~}+ Z' ^" U9 I9 w7 y% z7 t

7 ]: f, X0 B: N/ x; k7 o7 R; O0 O3) kthead_run(). R2 V( F! M2 O8 u! c6 a

+ U! N+ I( \* _# U. `3 ]注意到mmc_init_queue这个函数的最后,创建并运行一个名为mmcqd的线程,顾名 思意,mmc queue deamon它是一个SD卡的处理请求队列的守护进程,或者说内核线程,当系统注册SD卡块设备驱动时,就通过mmc_init_queue()开启了这 个内核线程。$ l: [9 j$ h, T: s" X8 \6 m
& m# w' m; ?, B1 d4 g
4) mmc_queue_thread 5 B/ [. ]* a. F$ K

0 ?0 T% b$ k! U; h3 C- V看看这个内核线程做了些什么,
1 b7 |+ ]2 n( d) u, E  H/ v9 F
0 r2 b" s4 r9 B. s: m1 ystatic int mmc_queue_thread(void *d)
1 L# g5 `3 L8 n+ l; I; E{
0 p! M( w0 o1 x9 H5 c struct mmc_queue *mq = d;
  F" ~0 i# T& ^1 w+ e& ` struct request_queue *q = mq->queue;+ I2 C. I, N1 a. q

$ u, b5 c- g) m5 ` current->flags |= PF_MEMALLOC;% D+ Y% f, S! \+ r6 W9 P

- ^( g, g) L! Z, o! v down(&mq->thread_sem);( U4 H9 b+ o. u; b- f* i; J

, g; _8 G  M5 g1 P  T/ e+ A; N8 H6 e% @/ v! p  [
do {
) Z1 |& n- `" n: A  struct request *req = NULL;- x) @" e7 p0 R

. b4 `( \2 W# J  spin_lock_irq(q->queue_lock);9 I: y" T& Y* d  q/ Q
  set_current_state(TASK_INTERRUPTIBLE);% p& `/ a# Y* S5 }
  if (!blk_queue_plugged(q))1 I9 e0 w3 i8 n( Y# s9 _( g7 ?. e
   req = blk_fetch_request(q);1 [3 b) p: q: A' _# @! M
  mq->req = req;
9 n+ ^+ j  _. j4 T  spin_unlock_irq(q->queue_lock);
% t1 S  M, P' z4 z
$ b) N" {4 U9 F8 H  if (!req) {
+ a: J) `6 c0 J* i& h5 s2 y6 w% j/ x  F3 N! ?
   if (kthread_should_stop()) {
) |0 x( y4 Y  W* a    set_current_state(TASK_RUNNING);
- Z4 h) D8 J* l& e    break;5 Z5 [" i; e/ t. n* _
   }4 Z1 s& W( Y( D) S& R8 ~* [
   up(&mq->thread_sem);
$ Z" Q) a. [' z! U* _! l: k   schedule();9 p) v$ R3 m3 Z! G' g" A/ I
   down(&mq->thread_sem);
" U/ d* V3 H; I: N0 T   continue;; m* W; t+ ?% J9 N& p7 {
  }
. i7 _8 h. L5 ^# Q7 v1 a8 V  set_current_state(TASK_RUNNING);/ `% {; X$ V8 g9 ^9 Z
+ f8 U3 X; ]- w9 H
  mq->issue_fn(mq, req);
6 |+ _$ g, k/ M: u2 b } while (1);& B& q! f& M! T. g3 i
, q6 O% ?( b2 |- a3 _9 b
1 b7 I' ]+ h% M+ L; e4 X9 d0 T% J
up(&mq->thread_sem);
8 m0 H5 q* m+ `7 P% f0 x1 V9 h( f7 m6 y2 n3 `
return 0;6 P6 S" ^9 N7 E( b
}5 K% j5 f9 s/ `% j# \8 b: r" n

7 i4 `4 h6 T' j) P( C6 k. v首先,这个守护进程是一个while(1)死循环,如果没有特殊要求,即kthread_should_stop()指定要把这个内核线程终止掉,那么它将从系统启动开始一直负责处理SD卡的请求队列。
% K5 o3 @! l3 e4 x$ [
( D- C$ J6 r  R* M6 J9 b, p/ @在循环内部,内核线程首先通过 set_current_state(TASK_INTERRUPTIBLE);设置当前线程为可打断的等待线程,进入睡眠状态,等待其他线程唤醒它,这 里唤醒它的就是处理SD卡请求的mmc_request,当mq->req为空,即当前没有请求正在处理,则通过 wake_up_process(mq->thread);唤醒内核线程,接着该线程尝试从请求队列里得到一个请求req,
" i7 z8 d; ]+ a0 S. a+ z9 l1 r' \/ |) Y1 n' m, Q
-> 如果没有请求,则调用schedule()交出cpu的使用权让其自由调度,等到系统空闲时,再次得到cpu控制权,并且执行continue;退出当前循环,重新开始新的循环。3 {1 {/ _/ ?$ z

3 U- e+ M# e; ~0 g. Y" T. s-> 如果得到了一个请求,则通过set_current_state(TASK_RUNNING);将该内核线程设置为当前正在运行的进程,并调用issue_fn(),即mmc_blk_issue_rq,处理这个请求,实现主控制器与SD卡的数据传输。& w4 |) d- V& F  {

  M4 J; W& H$ y$ x2 J5) issue_fn
4 I& k8 ]3 I5 A# X" D1 Y( i( ?! P  C" k; o" |, H: k% g# z
驱动初始化函数probe()里的mmc_blk_alloc()里注册了这个回调函数,md->queue.issue_fn =mmc_blk_issue_rq;# L8 W$ Y2 `" ]$ `
( L: f7 e4 A* F8 A
这个函数将req里的成员解析成为mmc_blk_request里的指令和数据,即mmc_command和mmc_data,然后通过mmc_wait_for_req()最终实现主控制器与SD卡间的通信。+ w6 j- O" Y# `* J2 G% [

: D3 k% l+ S" ?* {6 `struct mmc_blk_request {
6 |0 L9 h' m  |# J( V struct mmc_request mrq;# _1 F* G* [* o) j; }
struct mmc_command cmd;
9 C7 D( C! s8 h, \ struct mmc_command stop;
* K4 l+ q8 i" K) {% C* i struct mmc_data  data;
$ S1 {" E  F$ e5 j' V};
9 m, K) c3 k, l" Lstatic int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)* W+ L$ P4 l! w9 y( Z5 I/ X+ `
{5 Q9 Z3 V1 V/ d% L
struct mmc_blk_data *md = mq->data;& R, o3 G# s8 Q6 I7 \* P
struct mmc_card *card = md->queue.card;
! a: |$ O( b! D5 a3 q, T struct mmc_blk_request brq;
: r. p( |! S/ ~3 z) P2 M- n& X* b2 B1 p! F
mmc_claim_host(card->host);
5 r. D$ l7 N- q) K4 [% v: N# s% b/ z' ~
do {8 B) O3 J/ B9 J0 @+ ]6 m

% L7 r% s$ g6 _+ {4 [" n. Z7 a  }! i  brq.mrq.cmd = &brq.cmd;) I& ~( C, G5 H. S% z3 j3 g
  brq.mrq.data = &brq.data;
6 g5 W; y. r* m) O# y6 h
2 i1 K; f$ e2 v5 R3 d/ W6 J  brq.cmd.arg = blk_rq_pos(req);            // 解析指令的参数
  U7 V7 v2 J9 I1 U. q  if (!mmc_card_blockaddr(card))3 o# k) V1 N7 l
   brq.cmd.arg <<= 9;8 d( G* d# n5 _. t) m2 d! Z
  brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;5 I8 ]3 ]' V; H0 u6 Q" e
  brq.data.blksz = 512;0 s6 V' P# H- Y
  brq.stop.opcode = MMC_STOP_TRANSMISSION;! |' q: h# b) L, d( a4 D
  brq.stop.arg = 0;3 e5 u: @: N1 L) P6 P9 z
  brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;6 B. p8 W6 n* i0 D
  brq.data.blocks = blk_rq_sectors(req);  // 解析数据块大小
% s2 u' \$ a2 x1 ~! m9 ~0 i
4 Z) U% n! Z% I% S  if (rq_data_dir(req) == READ) {              // 解析传输数据方向
7 _7 n* {, l7 R4 z6 ^. ~   brq.cmd.opcode = MMC_READ_MULTIPLE_BLOCK;& b2 T6 D1 g6 p+ ?8 |
   brq.data.flags |= MMC_DATA_READ;
/ n7 l$ d  Z9 d$ d4 I7 t5 Y  } else {
+ O7 U" u' n+ q# q% D6 I1 j$ l1 Z, c   brq.cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
0 C! ~  h) d1 |2 f' M/ M+ [   brq.data.flags |= MMC_DATA_WRITE;
# o0 ^2 _) K8 f) Y  }
: L4 J- ~, u$ B5 z9 t6 M. m, m4 @- G. `/ _& w! v
   mmc_wait_for_req(card->host, &brq.mrq);  // 调用core协议层提供的接口函数,实现数据间通信+ [5 V3 Z' |8 l$ F/ g8 B
! J# ]4 `2 ?  M! f' @+ w1 \
   ... ...: U$ C6 R: L3 h- M  X

& g! I6 ^! n/ s; Y/ n. a* p/ p  }8 F/ b# L7 P6 p" T6 e' S# b
" f0 j$ Q- @) N* t1 V7 V# c7 Y
  mmc_release_host(card->host);1 {* d9 D3 F% w3 {9 D) \% Y
/ ?; ~) _" {5 P! g+ k4 \
}

/ }) {+ l. f( h5 _
' t1 |# T) a8 L; P$ p3 T# B
6 U' z; N8 }5 p/ a
4 M" ?* ]4 e% g/ W: n
1 ]- w% g' \  ]: j9 q( i

作者: yin123    时间: 2019-12-5 18:44
谢谢分享




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2