找回密码
 注册
关于网站域名变更的通知
查看: 320|回复: 1
打印 上一主题 下一主题

浪莎淘金----Linux设备驱动子系统

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2019-12-5 10:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

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
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-25 16:37 , Processed in 0.265625 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表