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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
Linux设备驱动子系统
, J, B6 ]  w* T4 s, b+ v  C! l3 j! F! [; u0 J& e
1. 总论
; y' c7 r$ S/ ]9 e) y7 P2. 主控制器
7 i7 P2 y8 x! e* ^1 A0 R: a7 @3. 协议层: V3 d2 H, Y9 Y( f9 V8 U, m
4. 块设备+ Z; D' n; w# f0 T& z3 \6 M  k; _
& s) H+ D) D/ u

& Z9 n5 V! @8 u! i9 N2 g1. 总论
6 `  k$ l% b; m0 r
9 |8 T+ W' P5 Q! O. }: R 1.1 概念0 M7 z: z% g" h; Z+ K
: L% n+ U3 F: k7 V; Y* s5 _
  • MMC - MultiMedia Card
  • SD    - Secure Digital Card
    2 L5 A6 M! s; n4 S; I6 L6 A

$ L: j! {/ u* z2 c$ S! z1 o8 v7 B# Y1.2 分类8 _$ U  ]; l; Q; ^7 |
3 x( {2 l& L5 i: Z
  • 按存储大小,普通SD卡(<=2GB,支持FAT12/FAT16),HCSD卡(>2GB,<=32GB,支持FAT32)
  • 按体积大小,普通SD卡,mini-SD卡,micro-SD卡(TF卡)4 r2 S  Z8 m" u0 s' |

$ |8 E) l6 u% }% H* Z3 L6 f$ z
9 C: ^" w! n1 ~1.3 速度
& S3 T) Q) W. D- O% v' _" h
3 p: P8 d' j- _8 \
  • 默认模式: 12.5MB/s
  • 高速模式: 25MB/s
    ( S3 q# f/ f1 T" U; h/ k

+ k0 V& y* |& s. t) E$ @# u
( {  f$ i, o( B. d5 ]1.4 子系统代码结构

# l5 s6 ^: M. U; m: n# i: l# P3 V: v+ K/ L# |$ e) s  t8 V1 E" d
     Linux源码里/drivers/mmc下有三个文件夹,分别存放了SD块设备,核心层和SD主控制器的相关代码,可以通过Kconfig和Makefile获取更多信息。
0 @% J& x) A2 u % {4 @" R: I% e1 u# G# `: z( {

$ B" T& y5 Q* X2. 主控制器
/ W9 b* Q0 u# f7 ]% M# h$ p3 z7 K8 f7 z
    SD卡的控制器芯片,可以看成CPU的代言人,它为CPU分担了完成与SD卡数据通信的任务。+ S, G3 V$ {. ^! M6 Z. f5 }
4 ]- D1 g6 u5 H' G
2.1 数据结构6 V+ r. Q! }& V4 @1 o: v2 g( B5 p

8 |; X$ e; E; e- a: E1 _" ~     以PXA芯片的SD控制器驱动为例,
2 N/ Z0 {" P/ J" U, e4 g
4 W% |! X" [9 y" L$ fstruct pxamci_host {
9 Z4 N3 C: n- F: c+ l struct mmc_host  *mmc;! ~9 \( V/ N2 i
$ F3 e& ]5 e- `
struct mmc_request *mrq;
0 j" Q9 B2 U/ p& F; m" B struct mmc_command *cmd;
# G9 ?+ x1 T. \( Q struct mmc_data  *data;
9 a) a. d! d' v* v, i1 K) r0 c) t& k, Z3 a* M- ~
... ...: X/ f! x$ |) g3 m
};
0 p; X: [7 F" I& J; x/ \+ O
; C3 G: u+ V; E2 F2.1.1 struct mmc_host
, V/ F" t4 a' H, f+ B: }4 [+ a6 G
* D7 k; {* q+ a( b3 Z. m     结构体mmc_host定义于/include/linux/mmc/host.c,可以认为是linux为SD卡控制器专门准备的一个类,该类里面的成 员是所有SD卡控制器都需要的,放之四海而皆准的数据结构,而在PXA芯片控制器的驱动程序pxamci.c中,则为该类具体化了一个对象struct mmc_host *mmc,此mmc指针即指代着该PXA芯片SD卡控制器的一个具体化对象。
$ r6 e: M+ ^: {4 E$ I: R8 s: P6 M
struct mmc_host {
1 s+ b9 K& I" n9 l+ { const struct mmc_host_ops *ops;     // SD卡主控制器的操作函数,即该控制器所具备的驱动能力3 Q+ w: V. s8 J
( U  [  i2 g* t5 F, E6 I
const struct mmc_bus_ops *bus_ops; // SD总线驱动的操作函数,即SD总线所具备的驱动能力
6 F- Y3 q- O2 n( p1 V. _2 [/ l( F
  R; J: j/ I. V2 w% U1 _5 v struct mmc_ios  ios;  // 配置时钟、总线、电源、片选、时序等0 D) B; |3 P# Q( h2 V
( G# h7 ^3 J9 q6 Y2 }
struct mmc_card  *card;  // 连接到此主控制器的SD卡设备
; g2 A6 |% f- C5 k  a    ... ...
+ |/ t  D6 t$ d- p. T};  X" E6 O  E9 h

- U) ?8 B& y; ` - h8 X& E/ h( V+ s

/ U( z6 U; |3 k) o( G% h: ustruct mmc_host_ops {
7 `+ ^6 L5 @2 K void (*request)(struct mmc_host *host, struct mmc_request *req);  // 核心函数,完成主控制器与SD卡设备之间的数据通信! h3 ]: ~- ?8 y" D* b
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);  // 配置时钟、总线、电源、片选、时序等
, a2 c3 r4 v# m! u3 C" N! g int (*get_ro)(struct mmc_host *host);  e. D* X9 _; D6 w4 x* u6 r
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
8 S5 m' J/ b4 I6 n: G; }6 ]};
: v+ L( d8 `: V2 A
. c9 G" j$ c4 ~, t6 D& g1 G2 Ustruct mmc_bus_ops {
- g8 K  Y& v/ j: o void (*remove)(struct mmc_host *);    // 拔出SD卡的回调函数
4 y. I) P* l. P" C void (*detect)(struct mmc_host *);      // 探测SD卡是否还在SD总线上的回调函数! H# v8 d7 q. W" V
void (*suspend)(struct mmc_host *);
! |& d8 C( y5 c3 z void (*resume)(struct mmc_host *);$ m  b7 m0 q/ B( P8 T
};3 G4 `7 V5 J# Y
9 u& H0 o, w  E7 _
struct mmc_card {* Z, F# o- P  Z0 d+ G; ]( x
struct mmc_host  *host;  /* the host this device belongs to */
" h9 H  B* ?$ y% z2 E, a) D struct device  dev;  /* the device */
- P- M" l' c: S# g4 M unsigned int  rca;  /* relative card address of device */- R7 r6 y$ O3 n7 ^
unsigned int  type;  /* card type */
- |7 U. W- l- z; i unsigned int  state;  /* (our) card state */+ q) u0 ]( F% k7 P: q2 e; u
unsigned int  quirks;  /* card quirks */
9 B5 |+ ^% w& T: U8 p# [2 F) V0 [3 D
  j" \2 Q+ }! G0 D! t u32   raw_cid[4]; /* raw card CID */
  I4 M; n2 r! c$ i5 v: j7 o- L8 ^ u32   raw_csd[4]; /* raw card CSD */$ k  F( r  D/ M+ K1 M
u32   raw_scr[2]; /* raw card SCR */
! K+ J% g# b9 d struct mmc_cid  cid;  /* card identification */+ z4 U2 H/ h/ D' y8 @: g! r6 T
struct mmc_csd  csd;  /* card specific */
- Q& _6 e8 \1 v8 M( `( o% M struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */# r) P% H, _# [8 k- X9 D) c. _  H
struct sd_scr  scr;  /* extra SD information */
. Y% E" h/ C( `: \0 U3 B0 |' H1 E9 V0 } struct sd_switch_caps sw_caps; /* switch (CMD6) caps */: Q5 Q1 v, P, X6 |) O5 t. w+ G
* s1 V  e# D) ~: s' ^% t& z
unsigned int  sdio_funcs; /* number of SDIO functions */
- N) [7 w9 V& q6 J# }' K! o struct sdio_cccr cccr;  /* common card info */+ v  d9 Q" T1 G/ k' Q
struct sdio_cis  cis;  /* common tuple info */
5 K3 @. h; ]6 e" }   ... ...
$ [6 L, Z) [! g};
& u0 @5 o9 ]9 O9 I" nmmc_card结构体内的数据结构主要存放SD卡的信息,其中RCA, CID, CSD, SCR为SD卡内部的32位寄存器。* j$ C' F5 C) D( @; x
- I9 ?( m8 R& J7 w9 t: D
# D* G6 w7 i( V: b" z* V
2.1.2 struct mmc_request# h# R" t' M$ }% P$ [! [

1 {" j0 e& `6 [' u1 I# O+ N0 z      结构体mmc_request定义于/include/linux/mmc/core.h,它主要存放两大数据结构的指针,分别是cmd和data,顾 名思意,一个为指令,一个为数据,也就是说,mmc_request结构体存放了进行主控制器与sd卡间通信所需要的指令和数据,struct mmc_request, struct mmc_command *cmd, struct mmc_data *data三者之间的关系如下所示, 4 w& `) ^6 u- v7 F: e( V7 L# D5 x
: @! P0 ^; F. h: D/ j
struct mmc_request {
4 b/ s3 @" R7 U* V! b struct mmc_command *cmd;( h% I; L2 v. _5 N% N$ J
struct mmc_data  *data;3 g* z3 T7 i& }5 g
struct mmc_command *stop;
2 y+ V' V; {2 w, p' X! e- e! I2 x$ }: V2 a2 |2 p* I
void   *done_data; /* completion data */
0 h% ~( h" K# ?5 O: B& M void   (*done)(struct mmc_request *);/* completion function */
. x' n8 L7 _  o0 X  U};0 ?/ T, t( T- _& K5 \
* s9 x1 O) i, [+ r" A% @
   说到结构体mmc_command和mmc_data,就必须说说SD卡的协议了。+ H* y3 C- E1 l9 Z; F3 u% H' ^. M
* H: o# A* r( X2 v' e8 y0 y/ q
   1) 物理结构! t. q2 N+ ]: s/ R% L" B9 u
( N3 B0 h" W3 e2 x
     SD卡有9个pin脚(micro-SD为8个,少一个接地pin脚),如图所示,
, M) ^; f! k/ x" d+ }9 U
# H( ?: s: E% K& U: [1 c# L
1 V% w( y  p) q4 x1 W7 m1 {5 @8 y1 j2 d3 z! Z
   SD的数据传输方式有两种,普通SD模式和SPI模式,以SD模式为例,9个pin脚分别是VDD,VSS,CLK,以及我们需要关注的一根指令线CMD,4根数据线DAT0~DAT3。
) Y( U0 f: L" B7 X7 P4 B+ D+ k1 m0 F& _% g' X9 g
   2) 传输模式
# a/ k/ C5 v  O0 ^. s' z6 s3 h! _5 l4 n
   首先由主机向SD卡发送命令command,等待SD卡的回复response,如果成功收到回复,则进行数据传输。其中,指令线和数据线上传输的指令和数据都要遵循相应的协议格式。) d( V. [) b3 {
2 m' `& A9 a8 K) D5 A: O: U1 Q8 J

$ b* d' r% D7 d, |: U1 c$ n
  ]1 }. Y% Z+ ~; m   3) 指令格式, z4 A* O) }, A- l

7 }2 X) O) z6 r. ^- Y* _% [  % Y) v2 Z6 g  k
+ w( u( a" ^+ K( X
   一条指令command共48位,其中command index指代这条具体的指令名称,argument为该指令的参数。9 x( n. N2 I: L% g/ m6 V; P  a

6 x: J0 x/ J0 H5 Q, z1 F' h! i8 ~4 r   一条回复response根据不同的指令有几种不同类型。
5 d2 ~: Y* F# ~! [9 a
  z4 o, x8 D- K1 \4 N" X7 k  @' [struct mmc_command {
6 x0 H7 u, @$ B8 o4 e3 p  s* w/ ] u32   opcode;            // 对应command index- V) ~% W7 ^* l
u32   arg;                  // 对应argument$ J+ k- ?+ M1 r
u32   resp[4];           // 对应response, A6 Y) i+ y% R; H
unsigned int  flags;  /* expected response type */8 f- v% Q. B4 u* K) h9 ?! }

. r6 {  p  g& l1 X ... ...! V1 i1 f3 C! |: g: u) P% ]
: ?! ]& \6 c2 |+ w
unsigned int  retries; /* max number of retries */
9 j7 A. ]8 }, \! ]5 I1 S# L' ` unsigned int  error;  /* command error */
" V; x! l7 o" G( \
5 D  U8 a0 v6 M6 Z. y: _ struct mmc_data  *data;  /* data segment associated with cmd */+ v: e- X* l9 i" F; N3 G2 B
struct mmc_request *mrq;  /* associated request */
- D- |% M/ I: O  [! K}; ( Q7 C# |& w- E8 S2 s3 F
  l7 C1 X. r+ @( Z
   4) 数据格式
: U1 l; B8 ]' @/ V; i# `6 y1 k3 z& W$ _7 r
   数据传输按数据线可分为一线传输和四线传输,按数据大小可分为字节传输和块传输(512字节)。) S' [  w* v% J( ^! R( l/ q" Y3 M

- Z* g( S3 _; O( H% Kstruct mmc_data {
+ c7 k( s: [9 [9 u unsigned int  timeout_ns; /* data timeout (in ns, max 80ms) */* D+ e9 |/ J; |) ?, \
unsigned int  timeout_clks; /* data timeout (in clocks) */
3 y9 p) v3 o$ G# z1 J9 N  m unsigned int  blksz;  /* data block size *// ^$ X1 o( w6 x" X& N" k" `
unsigned int  blocks;  /* number of blocks */3 K( ?+ U) a0 g/ {" }8 X
unsigned int  error;  /* data error */
0 I4 U* W+ R' ^& o9 V6 ~' \7 w) h unsigned int  flags;
  F) c6 s- m/ t1 C1 g
6 A3 q* h3 }' x! k; {#define MMC_DATA_WRITE (1 << 8)
. ]( H9 L( h; J2 [#define MMC_DATA_READ (1 << 9)
$ \" x. S( l  Y#define MMC_DATA_STREAM (1 << 10)
3 {% g1 V$ Z8 N; ?4 ~( ^0 K; H  H; Z
unsigned int  bytes_xfered;8 k- ^2 \, @  J- x% O: ~+ U
: D/ t4 o$ E& A2 y0 G
struct mmc_command *stop;  /* stop command */
! o) i  j5 P9 j struct mmc_request *mrq;  /* associated request */
8 L" @( r$ d- ~2 m6 ?2 I  y$ h" ]
% C- _& c, e2 A5 S( H unsigned int  sg_len;  /* size of scatter list *// ?, `6 j3 E4 `( a
struct scatterlist *sg;  /* I/O scatter list */
) w7 ?2 @8 Q6 \};1 D, ?6 a1 R# P- ]

6 U. T5 t8 O9 n) g2.2 驱动程序
( r. M) R$ q7 k5 V7 @3 F& a, l  O8 b4 W, }4 C5 y
   系统初始化时扫描platform总线上是否有名为该SD主控制器名字"pxa2xx-mci"的设备,如果有, 驱动程序将主控制器挂载到platform总线上,并注册该驱动程序。. E& q, x% t% ^; G5 q

& o: d9 J5 t  z9 W, I static int __init pxamci_init(void)
2 x5 k6 U: E$ U; U  N1 t; v* z/ p: n$ P{
- |& P% {) T; S& p. v% }/ Q: s1 B% p+ M! q# q
  return platform_driver_register(&pxamci_driver);' x8 k  h! ^: M: T
}
& @6 D5 `; ]2 p; s. d; w9 f9 W% l- L1 v1 V
static struct platform_driver pxamci_driver = {
& S2 \- W' _6 _5 o' c+ ~ .probe  = pxamci_probe,
1 j0 @. d  P/ Q" s+ A .remove  = pxamci_remove,
5 D7 A4 F4 `# ^0 ~& _7 X .suspend = pxamci_suspend,: `' [2 n' m7 [) ~: X
.resume  = pxamci_resume,& q. Q4 ?) x* }; C
.driver  = {$ Y1 z5 t1 b, k2 @5 k5 N
  .name = “pxa2xx-mci”,
+ q9 i& \; I6 z# ?# s  .owner = THIS_MODULE,
5 C+ H! V0 P9 `% i4 v+ m+ a },
+ A% P' n& _% G0 H/ S! k" V};
7 C% A! T" [& b5 Q! ^6 d  ~$ z! U1 ?, S/ k3 n# A0 S
其中,remove为probe的反操作,suspend和resume涉及电源管理的内容,本文重点讨论probe。. E) q5 t/ e0 I5 u# T  A7 R

" `5 ]+ o, S" _. X6 k" QSD主控制器驱动程序的初始化函数probe(struct platform_device *pdev),概括地讲,主要完成五大任务,
" V& z& o+ {! i- ]# b" a
3 ?6 \( Y) m; ~/ ]/ U, f
  • 初始化设备的数据结构,并将数据挂载到pdev->dev.driver_data下
  • 实现设备驱动的功能函数,如mmc->ops = &pxamci_ops;
  • 申请中断函数request_irq()
  • 注册设备,即注册kobject,建立sys文件,发送uevent等
  • 其他需求,如在/proc/driver下建立用户交互文件等
    0 z% r# J6 j- [# ?' s" j
4 g% y$ X( {: F2 q
; f* L3 c/ V; I4 a0 z9 J
2.2.1 注册设备
1 i' z. }! L2 g1 _; S8 R+ r: r" ]2 U5 o, L
    对于设备的注册,所有设备驱动的相关代码都类似。
" D6 h" n7 o5 B4 m+ O3 E
8 X) i: A) T- n; ]static int pxamci_probe(struct platform_device *pdev); u; y. r- `* F0 |3 R/ K

7 s9 a% ^$ C. g; Q& M3 Q- y: R{% C- b7 _/ D5 c7 |6 C, _: Y9 m- c

! F) ^# X9 @7 n; I- w5 `% ^. j  mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
5 M" U2 i, [+ ~" J: G, v$ F* A7 b( h2 g' S0 c
  mmc_add_host(mmc);. D2 d( G8 v6 }

) D( @) a# F6 ^4 U; A7 k8 B  ... ...) l  l  W% t* z, a
- K; K5 B8 X( [7 c+ A7 ?
}/ {, u0 i+ E. t3 w$ B' M
. [' O- g9 a! x# @! s/ p
这两个函数都由/drivers/mmc/core核心层下的host.c负责具体实现,
% J) T7 a( e4 i0 i5 X/ T
- F& O  \4 g! u+ k" J1) mmc_alloc_host
: Z' \  p: n" f7 W
+ h3 i2 T7 o1 p$ f3 S: d& a为主设备控制器建立数据结构,建立kobject,并初始化等待队列,工作队列,以及一些控制器的配置。其中,INIT_DELAYED_WORK(&host->detect, mmc_rescan);将探测SD卡的函数mmc_rescan与工作队列host->detect关联,mmc_rescan是整个SD子系统 的核心函数,本文第三部分协议层将对它作重点讨论。
5 g' L; d8 Y4 f5 D% r, y* l
- P1 w9 k: z& v. W" V( Q' istruct mmc_host *mmc_alloc_host(int extra, struct device *dev)
" _$ K" @$ o/ ]/ `& a9 K{
( g6 |" V5 E1 o8 Z3 c" u1 G. F$ P7 @/ c+ O% O6 \+ k
/* 建立数据结构 */
# z$ y: |8 F2 P3 j$ b) k/ @
; E: q8 `$ L  I  q4 ]& x struct mmc_host *host;
/ S( Y! O- b& f( R9 n
  @. M+ R' m! _& a4 f host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);# Q0 k9 P4 s5 L8 g" r- n  ]8 L
  [" [) v, F" D' ~- f
/* 建立kobject */
; u; M# Q; `3 h  @9 ~& ~4 Z
+ L; a/ A3 v' [; K5 `* w host->parent = dev;; e2 d8 g( m; `$ z: \6 i
host->class_dev.parent = dev;5 S/ \, r2 Z8 W1 H' d2 `: |
host->class_dev.class = &mmc_host_class;
+ I8 I/ l7 _5 X5 n, l1 i' g$ b device_initialize(&host->class_dev);
- Q$ b9 K' C3 t* T1 E% m; k& l) N
+ @9 e2 K8 G) `9 X! M4 \/* 初始化等待队列,工作队列 */2 r; r8 M0 P+ V- m7 O: f

) @  u' w: U  Y: r  a0 J9 [ init_waitqueue_head(&host->wq);: \$ O+ O7 S' Z$ X0 K( h* ]* V# i
INIT_DELAYED_WORK(&host->detect,mmc_rescan);1 u/ H2 D! d/ K* w) M" l1 P
* T+ s2 z3 a4 Q% r' [* j
/ |& B* W% s6 L% J# T6 e

' O- R% t- S# ^9 c! m; p/* 配置控制器 */) \5 U7 V7 ^3 [
- [% ?. u0 a! _- M' Z& W1 w
host->max_hw_segs = 1;$ }+ H" w" q! u+ w
host->max_phys_segs = 1;! v. d1 Q, ^; C! N1 t* _0 N. W
/ d! g7 B& a9 p. ]3 t7 V
... ...0 }- v7 a& t# A  e. U9 h& S
return host;/ p( \3 }1 ^# j3 P4 I
}
2 h# b+ V2 @" o: i3 e# A0 \, ~- X8 u; j  I" |3 k. D, j/ `" E
2) mmc_add_host; b6 P  o. H- n1 d* k0 P) S

- _9 |& i" @7 l5 `完成kobject的注册,并调用mmc_rescan,目的在于在系统初始化的时候就扫描SD总线查看是否存在SD卡。注意到这里的工作队列的延时时间delay为0,因为系统启动的时候不考虑插拔SD卡,关于这个delay将在下文讨论。
) ?5 M+ t4 [/ C/ M. ?9 j& g% {
9 P" `8 z2 M4 C) rint mmc_add_host(struct mmc_host *host)! b% M9 `3 g. E6 t7 h+ m: a7 X$ ?" J
{
& g. r3 Z7 p9 F" \6 N: {( N! t  device_add(&host->class_dev);7 Q. k& N  O1 X' M9 f4 r
  mmc_start_host(host);4 o7 R' M6 n- O$ x

8 q# d' n- r0 O' y3 @% n5 O  ... ...
: }8 N6 n$ j1 Q" Y. i& V+ |, u5 k% y7 U+ h7 \/ Q. S
}
0 a% {" H0 E" a# C. m% n/ p7 q* B( w. K2 X
void mmc_start_host(struct mmc_host *host)
- i, ?' N1 B: \( m) ^  C; H4 f{
. b" h! X% F3 n  Y$ E- k mmc_power_off(host);
* R$ n# E+ D# ?' Y  T" i mmc_detect_change(host, 0);
( u# q, o0 l4 C& ]4 o! t6 @}
7 L- @1 M/ x! `. {$ I: s: l
, z6 A. F9 N1 C& l, c- O9 k void mmc_detect_change(struct mmc_host *host, unsigned long delay)
, r$ f$ y9 a3 a2 V5 ~( w{
. k/ R# i  A* o. [' w* D mmc_schedule_delayed_work(&host->detect, delay);* S, D4 k2 C( Q; E' D' B* N/ i
}
7 v" M+ M3 a) I
. j. Q! H/ e# l, P! m. g! s static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
+ I5 X) w$ i, M1 v" D* @3 x4 o{
% J6 w0 E; B5 E( j wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ * 2);
7 Z. w" {+ r% L0 A! |. J. O return queue_delayed_work(workqueue,work, delay);7 t( I. S# L# r8 a- F2 S
}
2 Q% o. a  \$ P( ]$ F5 g) a5 w2 E0 q: {" v6 z
2.2.2 为设备赋初值

2 D) g& B6 \) F3 Z& |0 T( b   其实,整个设备驱动的probe()函数,其本质就是是为设备建立起数据结构并对其赋初值。pxamci_probe(struct platform_device *pdev)主要为SD主控制器完成时钟、存储等方面的初始化配置,3 J9 F' u1 y6 s- D, @
static int pxamci_probe(struct platform_device *pdev)5 w9 i( s+ \% l
{
7 v* e5 G# K. S" ]' q5 M struct mmc_host *mmc;5 c; j& g" i" U
struct pxamci_host *host = NULL;
' D2 J7 ?" [' o# |   V' w2 Q* O: _+ M; F' L
mmc->ops = &pxamci_ops;
. ]+ M. O6 S) s/ U/ a/ M mmc->max_phys_segs = NR_SG;
; z  R' P2 z1 y1 S* g& \ mmc->max_hw_segs = NR_SG;
1 ?5 E1 j' J9 A! ~, v# F mmc->max_seg_size = PAGE_SIZE;
* J2 O" p, }. j: d( g host = mmc_priv(mmc);: e- H7 j' H/ ^0 {1 Y) }9 ?0 G9 p1 v) \& |
host->mmc = mmc;
4 u& ~. M: `; y! u, { host->dma = -1;) H  _' @, z8 g
host->pdata = pdev->dev.platform_data;
& x$ S. ?$ m% n# n host->clkrt = CLKRT_OFF;
- v2 Z0 y2 S. d( r& } host->clk = clk_get(&pdev->dev, "MMCCLK");* C$ K6 W, ~' e9 B" ^
host->clkrate = clk_get_rate(host->clk);4 q% H9 n5 @3 f! K* j2 e3 {
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
. B" j2 u+ L" |% y host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);% a. d: X- L. |: o6 |
host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, pxamci_dma_irq, host);
; n, h0 P7 r9 g1 G7 M! |# r* ` ... ...0 N% c/ S$ l, r8 X* G
}5 M1 U4 p3 g! H; M% l4 M' Q
完成所有赋值后,通过platform_set_drvdata(pdev, mmc);将数据挂载到pdev->dev.driver_data。
+ M2 ^/ ?; A, `+ D1 @; C: v! ^所有赋值中,我们重点关注从platform_device *pdev里得到的数据。platform_device *pdev是在系统初始化的时候扫描platform总线发现SD主控制器后所得到的数据。8 H9 i2 r- M) \& d: i  b# f
1) 得到platform_data数据
1 Y$ w1 z2 _, E5 E: y& x4 t' U* Q% l先看看platform_device的结构,0 `9 a4 F3 p9 l0 ]1 c4 t. b
struct platform_device {
) P/ c$ f; J( R const char * name;$ Q# s1 j! I" `" \* l
int  id;( c  a& Y$ X! D: v1 h' ?
struct device dev;
& [4 N, h* {' F' X u32  num_resources;5 f$ X6 a, N2 I) e: }
struct resource * resource;  V# T' R3 T/ |  `( w  _. y2 }
};
7 W5 z4 ~7 X; }系统初始化的时候,已经为该SD主控制器的name, resources等赋上了初值,具体内容如下,
; m$ u! S0 t) l' _& Rstruct platform_device pxa_device_mci = {/ A' ~' k4 ^- H( Y3 z, I% x3 ?
.name  = "pxa2xx-mci",
5 `- I, D4 C4 o$ `9 q .id  = 0,
5 y- c3 @( a  N* O .dev  = {
9 N& V+ H, t: Q% A+ `  .dma_mask = &pxamci_dmamask,
0 m# s' W+ ^( a8 M# |; k6 c  I2 J( y# H  .coherent_dma_mask = 0xffffffff,
  I3 s# r0 g/ }9 J( {1 | },
, j4 G1 F/ h4 Z7 ?: k .num_resources = ARRAY_SIZE(pxamci_resources),
+ O6 [2 H* N4 j( L .resource = pxamci_resources,8 L8 H8 S# Y$ z: G# u# J9 R
};
3 \6 `. s. f' _1 Vstatic structresource pxamci_resources[] = {
- w( b( U8 K# T" q3 M [0] = {
" ^' d4 `& s/ E, Y; x* o7 N  .start = 0x41100000,
+ d, c2 \/ R. e  .end = 0x41100fff,7 h& ?+ f0 e* B1 ]
  .flags = IORESOURCE_MEM,  // SD主控制器芯片的起始地址
) f  A/ T3 [- n1 L, g- } },
5 ^" x" ~5 Z3 }) ^# A, u* o [1] = {
  p5 a) W! v" S6 T1 ~/ r/ E  .start = IRQ_MMC,          /* #define IRQ_MMC  23 */
# _" V9 i; L, |0 u' n, ~  .end = IRQ_MMC," T- R, N8 d. @" g# w: }
  .flags = IORESOURCE_IRQ,  // 申请的中断号: F) \/ y0 X6 u# `; v, Y
},3 C( E8 T! W+ J- L7 c+ }
... ...
9 m  s7 A8 z% x  j9 I};' g& j6 W- j4 B- r
  需要注意的是,platform_device数据结构里的name, id, resource等是所有设备都用的到的数据类型,那么设备自身独有的特性如何表现出来呢?事实上,结构体device专门准备了一个成员platform_data,就是为了挂载设备的一些特有的数据。(注意与driver_data相区别)  p/ H8 f" ^4 |
structdevice {: g; U& W# R, A  v) S2 X
  void  *platform_data; /* Platform specific data, device core doesn't touch it */# x( [% ^3 I, M: j7 d
  void  *driver_data; /* data private to the driver */% p0 M1 p% {' Q" n/ ]
  ... ...* U" U) ]2 X3 n' i9 A6 J5 x
}
7 b! j5 E- I) S) o  看看SD主控制器为什么会有这些特有数据,3 @$ b% K2 V9 Y8 [! y4 E
static struct pxamci_platform_data saar_mci_platform_data = {6 C( S8 B" u8 u
.detect_delay = 50,
1 N; Z2 K, M& y$ t: L0 G0 v, M .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
& x4 j& x# L8 {. s' O% p( s .init   = saar_mci_init,1 f* ^8 l& n5 _
.exit  = saar_mci_exit,/ J" |  u9 q' a) I; y; H  a( \0 h4 |0 w" l
};) x1 ^4 c& u# g9 C7 w# {
-> detect_delay* l( u- Q& f% V6 Z8 @+ D# s8 Y  @2 S
   就是刚才提到的工作队列的延时时间,设置为50ms,由于各种SD主控制器芯片的性能不同,这个值可能会变化。那么为什么要为工作队列加一个延迟呢?首 先,当插入SD卡之后,SD主控制器上的探测引脚会产生一个中断,之后调用中断函数里的工作队列,然后执行工作队列里的mmc_rescan去扫描SD 卡,为SD卡上电,发送地址,注册驱动等。考虑到插入SD卡需要一个短时间的过程(有个弹簧卡 槽固定住SD卡),如果没有延迟,那么插入SD卡的一瞬间,SD卡还没有完全固定到主板上,系统就开始执行mmc_rescan,那么就很有可能在为SD 卡上电、发送地址的过程中出现错误(拔出SD卡同理),因此,必须要有detect_delay这个值。
9 F/ U! X+ V% @0 F7 ?7 d3 ~) L# r4 u! i/ T2 C* x3 M6 t
-> saar_mci_init4 S4 G) F! K7 R
这个函数为SD主控制器的探测pin脚申请中断,具体内容将在下文中断的一节中讨论。- O& Z! m6 Y" L/ i8 V
static int saar_mci_init(struct device *dev, irq_handler_t saar_detect_int, void *data)( ~  M/ B: X5 {; h
{
1 L( [% Y  ^) l/ H8 }2 b: M$ T7 x, j struct platform_device *pdev = to_platform_device(dev);
# s7 |2 m4 N+ E& |  `( ^ int cd_irq, gpio_cd;  // cd - card detect; v1 g( S* e5 {/ ?5 l" r! Z% e( T

, N; Q/ x$ k, ~- R+ I- F1 T saar_mmc_slot[0].gpio_cd  = mfp_to_gpio(MFP_PIN_GPIO61);  // 将GPIO61设为普通GPIO口
0 ?5 a, h3 L3 E+ I cd_irq =gpio_to_irq(saar_mmc_slot[pdev->id].gpio_cd);   // 将GPIO61转换为中断号
: S2 N$ t& X1 W gpio_request(gpio_cd, "mmc card detect");  // 申请GPIO61
- D$ Y: D# m. { gpio_direction_input(gpio_cd);  // 将GPIO61设为输入类型; I/ P0 e2 V! d' P. l6 M
request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "MMC card detect", data);3 X8 ?) F- e& ~7 O( |3 e
... ...! P3 q4 q2 G, S. b+ G' h+ L4 f
}# o- s2 V  J$ o& y
得到SD主控制器特有数据后,将其挂载到dev.platform_data下,并最终完成对platform_device *dev的注册。
# f. {1 X& Q2 g' X4 Jvoid __init pxa_register_device(struct platform_device *dev, void *data)$ R0 g% f' h9 M( M: ?0 b: t3 F& N
{
7 u+ A1 i7 k: g4 _; c# ~ dev->dev.platform_data = data;
/ i1 N8 }1 c! |5 f5 w6 h: I platform_device_register(dev);
' \- f1 \; Y- l}3 w# m. q: U& I: n7 a
2) 使用platform_data数据! D, {9 q$ s6 Q
  下面就看看SD主控制器是如何使用这些在系统初始化的时候就已经得到的platform_device的数据的," i& ?" }8 j- B" Z) k
static int pxamci_probe(struct platform_device *pdev)* u% i3 d" J. `4 i1 V. s
{2 S* h5 I! E  ]
struct mmc_host *mmc;
0 l9 t$ j& s3 y, L+ B* {) U struct pxamci_host *host = NULL;8 \( R2 a& W+ O: z0 ^' `
struct resource *r;+ g$ b8 a% r/ T: l
int ret, irq;
6 J2 A3 b! E; |3 p' N' c' w+ Y0 J; M r =platform_get_resource(pdev, IORESOURCE_MEM, 0);    // 得到控制器芯片的起始地址8 ~" l5 K9 n+ ~2 W1 ^' b
r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); // 为芯片申请4k的内存空间
3 X; X. G! x1 ~ irq =platform_get_irq(pdev, 0);  // 得到芯片的中断号
+ o7 B7 W  y0 p6 A, a  `: d: A0 W host->res = r;& O) R- }8 V( V: Q" B$ g( m
host->irq = irq;  T: L" a4 V) u9 _- e' Z9 G' e' X
host->base = ioremap(r->start, SZ_4K);// 将芯片的物理地址映射为虚拟地址
# b2 D$ I5 I5 a4 D$ C! N- | ... ...8 n; T/ P( D3 w$ M9 O
}
, i8 W9 L  M6 p
' _7 w( I* T2 N& |
2.2.3 设备驱动的功能函数
" }6 ?* ?& k- A: O- ~; F* ]" j
   一般情况下,设备驱动里都有一个行为函数结构体,比如字符设备驱动里的struct file_operations *fops,该结构描述了设备所具备的工作能力,比如open, read, write等,6 j5 V+ r! d% B5 @, V* z0 \+ k" R
struct file_operations {% r7 ]* l* }  Q, ^: v+ {# ?% u6 }" H
struct module *owner;
& {, Z4 u* J  O5 I4 z ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
; ~8 g& j& ]( ^& Q ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);" T# M9 t2 n/ T7 R/ u
int (*open) (struct inode *, struct file *);
4 d% |9 p+ g* }% W ... ...
7 A: k( n4 q  f};. T( D4 |/ ?4 q$ b; O2 R% h, ~# n1 c
  同理,SD主控制器驱动程序里也有一个类似的结构struct mmc_host_ops *ops,它描述了该控制器所具备驱动的能力。
. s3 B2 N6 r+ C$ ^) G; nstatic int pxamci_probe(struct platform_device *pdev)1 O" V& V+ d: N. c0 f: [
{
# N- o# P  w0 |: b: s( }  y  mmc->ops = &pxamci_ops;, ^" A2 p7 }! t/ F- i
  ... ...5 D& O% A8 x$ e3 I" M' f$ n
}
( Y6 `0 ?9 }5 M* i' Wstatic const struct mmc_host_ops pxamci_ops = {# {/ p0 u5 f; Y9 W0 U: `
.request  = pxamci_request,
( \" J# b- J" K+ p .get_ro   = pxamci_get_ro,
7 r. i# |- T; a& O3 R! ~* n .set_ios  = pxamci_set_ios,: n5 }; `0 y" Z
.enable_sdio_irq = pxamci_enable_sdio_irq,
7 M1 j0 W/ S1 ~3 d$ _. ]};) c# z' P5 _; S5 X! {$ v
其中,(*set_ios)为主控制器设置总线和时钟等配置,(*get_ro)得到只读属性,(*enable_sdio_irq)开启sdio中断,本文重点讨论(*request)这个回调函数,它是整个SD主控制器驱动的核心,实现了SD主控制器能与SD卡进行通信的能力。) l, L# m, c6 w7 I5 J! r5 I
static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
. C0 C- I% K9 p, s{; O8 l8 k( n- J* q. h
struct pxamci_host *host = mmc_priv(mmc); unsigned int cmdat;+ ~/ ]$ S- G! u. f3 v( @  s( Y1 U

& s! L) d5 w) b: Z* q4 _ set_mmc_cken(host, 1);7 M1 z% B" A; q! x
host->mrq = mrq;
: L: k) j" w: I  ~" t. q cmdat = host->cmdat;
; L: m' P' N( R$ ^! G. U  o- o: J* R host->cmdat &= ~CMDAT_INIT;3 W2 ^# e- T" ^5 _# G
if (mrq->data) {
' g. o' {3 G- f  pxamci_setup_data(host, mrq->data);
: b% J; |( a9 F( u' v; {  cmdat &= ~CMDAT_BUSY;
. ]" t1 [: s5 G+ k/ c3 |# A5 b  cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;. D5 _  N- M% B" K$ r( y9 X
  if (mrq->data->flags & MMC_DATA_WRITE)" e' j6 t- L7 j- U8 z& J* \
   cmdat |= CMDAT_WRITE;
3 J+ i0 w/ |8 u: u3 Q: O8 A  if (mrq->data->flags & MMC_DATA_STREAM)- K/ ~  m0 i* r* }  [8 X
   cmdat |= CMDAT_STREAM;0 {; n. ^6 w( N: a
}: ]8 Y; F. Q8 Z2 N& J" [" q
pxamci_start_cmd(host, mrq->cmd, cmdat);& b6 _% f4 |  R- h3 O
}' \1 Z) V6 e6 O. L5 \' Y
其中, pxamci_setup_data()实现数据传输,pxamci_start_cmd()实现指令传输。  D, r& q! M6 [. e) X, B9 `6 O' S7 S
至此,我们必须去接触SD主控制器的芯片手册了。4 h& H9 j% S# t' z

9 }4 |! d4 b- d' Q7 r" O- P首先,SD主控制器由一系列32位寄存器组成。通过软件的方式,即对寄存器赋值,来控制SD主控制器,进而扮演SD主控制器的角色与SD卡取得通信。
. K+ P1 d) W* D7 M
3 z0 d1 V3 c* u0 F+ ]+ x1) cmdat5 ~: a! m* k3 ?9 H- h( X
  根据主控制器的芯片手册,寄存器MMC_CMDAT控制命令和数据的传输,具体内容如下,4 E  ~8 S3 {) I9 D1 O

0 _; a9 z# G5 A- \* N' I结合对寄存器MMC_CMDAT的描述,分析代码,- O) s% n* s' ?
host->cmdat &= ~CMDAT_INIT;               // 非初始化状态7 x( M' l1 I% v3 ]/ X% t
if (mrq->data) {                                       // 如果存在数据需要传输' ~+ X! o: C  I. c4 b7 _; w9 S
  pxamci_setup_data(host, mrq->data);  // 实现主控制器与SD卡之间数据的传输
9 P3 @0 r) G8 k+ X2 X: G  cmdat &= ~CMDAT_BUSY;                      // 没有忙碌busy信号! s6 d7 \% t& h! o; t
  cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;   // 有数据传输,使用DMA
' ]4 I  O/ n+ j! U! y- x3 g$ P; k  if (mrq->data->flags & MMC_DATA_WRITE)   
& g$ _( c: v. N6 U8 Y+ L   cmdat |= CMDAT_WRITE;                               // 设置为写数据1 w1 x* H9 x4 o: r$ z
  if (mrq->data->flags & MMC_DATA_STREAM)0 Q" m, P; R8 C0 ~/ N1 l: P. ?
   cmdat |= CMDAT_STREAM;                             // 设置为数据流stream模式0 w% j2 u0 e, Z
}
6 F, d1 W5 c$ A7 P% p$ r( @# d2) pxamci_setup_data  通过DMA实现主控制器与SD卡之间数据的传输
; K2 i; h8 c6 B: u; T, J' m' |" f; {static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
9 Y# ]( y, ~- u" d{/ _. ?5 ^' o, Z2 p7 E1 x5 h
host->data = data;
# N( V4 Y! ~" G3 Z' j( j writel(data->blocks, host->base + MMC_NOB);     // 设置块的数量: o$ b- N2 _6 _/ Q8 ^
writel(data->blksz, host->base + MMC_BLKLEN);  // 设置一个块的大小(一般为512byte)1 w/ x( A- P9 e0 P4 y2 q
  if (data->flags & MMC_DATA_READ) {
1 m3 ^' ^! o, U' {+ r  host->dma_dir = DMA_FROM_DEVICE;7 z3 o$ ^; @! q
  dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
2 ~1 ]) A& B* @) g# h3 {7 G  DRCMR(host->dma_drcmrtx) = 0;- P8 i$ \4 |  `9 w2 r
  DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD;. A9 r( B9 V; k
} else {: u) B% q% u6 A/ K
  host->dma_dir = DMA_TO_DEVICE;( s% ~9 y5 }& S/ e
  dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;! V; F8 y5 h+ ^2 I: U% I& `
  DRCMR(host->dma_drcmrrx) = 0;2 k0 W* T2 t$ P8 R: R( t
  DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD;
+ a& `, `- Q- [4 I  g" h3 d2 l) r4 I }4 L& G% j* S9 q) @4 j# B( Z
dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
' f1 Y7 C2 F2 E+ @$ z host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);( I% t, n+ a2 ]; P
for (i = 0; i < host->dma_len; i++) {
# z% l% [+ G, I. ]  unsigned int length = sg_dma_len(&data->sg);
, v. T& `4 |4 i* z' z  ]  host->sg_cpu.dcmd = dcmd | length;6 Z! T6 ~: C9 j: e
  if (length & 31)
/ Z- V( X' ~2 J3 Q- r   host->sg_cpu.dcmd |= DCMD_ENDIRQEN;
+ e6 e) \# E  @  n4 E6 J! D6 s  if (data->flags & MMC_DATA_READ) {
% f$ ^9 I5 d1 F1 X' N* ]* m1 h   host->sg_cpu.dsadr = host->res->start + MMC_RXFIFO;5 d) P2 l, v  r# S# Q; ~1 \
   host->sg_cpu.dtadr = sg_dma_address(&data->sg);) y; f# e6 Y: y; L; [: J3 o
  } else {
0 I8 H7 L/ |/ l% k3 A; _0 w& D   host->sg_cpu.dsadr = sg_dma_address(&data->sg);
' E8 `' U( [# P8 I7 |* R( s   host->sg_cpu.dtadr = host->res->start + MMC_TXFIFO;: X) }/ ?1 g# H6 {/ p
  }
8 a0 d4 D# ?( n  host->sg_cpu.ddadr = host->sg_dma + (i + 1) *( {5 R: e( Q) ~
     sizeof(struct pxa_dma_desc);
. g' L; n/ _  w* N0 i3 q }  k7 d! u' ^" D. e4 c( i9 g" G* E
host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
+ L5 w) L+ V4 t$ y6 @3 Z% q wmb();& n6 L; c) m2 \
DDADR(host->dma) = host->sg_dma;$ U" m# c, Y+ P$ ~) R7 ?( h" o" q
DCSR(host->dma) = DCSR_RUN;$ [& E5 ^  H4 q' G+ A0 W+ R
}
" d/ e8 }, y9 n# ], S, E for()循环里的内容是整个SD卡主控制器设备驱动的实质,通过DMA的方式实现主控制器与SD卡之间数据的读写操作。9 {8 [9 Q- L( x; A6 B" @
3) pxamci_start_cmd  实现主控制器与SD卡之间指令的传输3 \/ n3 v8 S! c! z7 V; u
static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
( F( }, t$ S) k, Y: G) ^  |, ^{8 d3 S* j* d% T7 n
host->cmd = cmd;+ g' o" j% G: w! m1 x
if (cmd->flags & MMC_RSP_BUSY): M! M6 b! W9 ?( @0 N- d8 U
  cmdat |= CMDAT_BUSY;; l' a2 |4 i! F( @# O) ?! |
#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
. D2 C9 |/ F9 u% R- `+ \ switch (RSP_TYPE(mmc_resp_type(cmd))) {
  b" g/ j9 L+ V/ f% P5 a* D0 x5 L  D case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */  }7 G* E4 n% J$ O9 d  x
  cmdat |= CMDAT_RESP_SHORT;9 k: c  X7 O+ M8 W, O' F
  break;: l* z' Y$ I- U& ^  `
case RSP_TYPE(MMC_RSP_R3):8 ^6 J' ^: B4 n) G' W
  cmdat |= CMDAT_RESP_R3;6 r# A# z" @. Z& q8 x
  break;
. u- \, B, S0 U case RSP_TYPE(MMC_RSP_R2):
' ]; a7 n9 d( s" X( @% O7 z& _% P  cmdat |= CMDAT_RESP_R2;" @4 K: @+ O2 P- ]; I  H- U: _
  break;
2 X' o: q+ E! s. Y default:& i- G0 H+ \' g) k1 e
  break;# v3 {& [! X6 }' C" Y3 \2 I
}6 W, X" j2 `- C5 e
writel(cmd->opcode, host->base + MMC_CMD);9 Z6 H' v) |4 b" y7 j5 E% \8 l
writel(cmd->arg >> 16, host->base + MMC_ARGH);( R9 R- y6 v, I# l  t
writel(cmd->arg & 0xffff, host->base + MMC_ARGL);  |5 N) b% F, \
writel(cmdat, host->base + MMC_CMDAT);
1 ~/ r" @* l5 L7 E3 ^0 O' p$ t) T5 B pxamci_start_clock(host);8 [) ^4 e& D0 N# X1 N
pxamci_enable_irq(host, END_CMD_RES);
" n) i9 d3 J  t5 x3 w! b4 q}
6 U4 F8 c, \8 I4 r, t# C-> response类型$ {  V0 k& [8 Z) C' X! R- q
  根据SD卡的协议,当SD卡收到从控制器发来的cmd指令后,SD卡会发出response相应,而response的类型分为 R1,R1b,R2,R3,R6,R7,这些类型分别对应不同的指令,各自的数据包结构也不同(具体内容参考SD卡协议)。这里,通过RSP_TYPE对 指令cmd的opcode的解析得到相对应的reponse类型,再通过swich赋给寄存器MMC_CMDAT对应的[1:0]位。. `1 f0 }4 j3 x# N7 ^
-> 将指令和参数写入寄存器7 F: p% [9 ^/ N$ C$ A; j$ K9 Y: E
  4行writel()是整个SD卡主控制器设备驱动的实质,通过对主控制器芯片寄存器MMC_CMD,MMC_ARGH,MMC_ARGL,MMC_CMDAT的设置,实现主控制器发送指令到SD卡的功能。1 ?# D8 q* h( ^. E+ J
4) 调用(*request)$ B+ E, s& {& b" p  ?) r3 n( s
以上通过pxamci_request()实现了主控制器的通信功能,之后只须通过host->ops->request(host, mrq);回调函数即可。  m6 U) i8 c0 L( u, i5 W
协议层里,每条指令都会通过mmc_wait_for_req(host, &mrq)调用到host->ops->request(host, mrq)来发送指令和数据。" q- k" e3 u7 }/ {5 }0 c$ B' a
/* core/core.c */
0 G6 d9 M7 d& Q' d8 D) j2 {void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
2 |0 f% p; ?) O! y" R: d  o8 n- U" P{
6 K6 H+ F+ r; K! P DECLARE_COMPLETION_ONSTACK(complete);
9 t! ~) o" t" _: g! U) \" i mrq->done_data = &complete;
( k. c- g8 c& F mrq->done = mmc_wait_done;
  H9 q1 l' K; F1 Z8 \" V mmc_start_request(host, mrq);! G1 s, I( V1 J5 M( J
wait_for_completion(&complete);
: V% W" B+ [7 I* E& t" T, j3 U}
( `0 Z4 w8 T" r1 ymmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
, I. F5 w1 V7 \5 h! j' ?, Y& \" t{8 V. h. T1 R% k& m7 ]# t
... ...9 l2 }8 ^+ H. b$ o  p- C2 `
host->ops->request(host, mrq);
2 g2 O/ t% z/ q* |4 w}
: q6 K3 `) q3 ^0 u+ a1 l1 G4 R
( M0 c" [% e. l4 q' x
2.2.4 申请中断

0 q' v) k* `/ A- e9 J1 _  pxamci_probe(struct platform_device *pdev)中有两个中断,一个为SD主控制器芯片内电路固有的内部中断,另一个为探测引脚的探测到外部有SD卡插拔引起的中断。
' X3 S2 j! ~: }1) 由主控芯片内部电路引起的中断# E+ j% [6 \/ G8 ]9 @1 q( t$ d
request_irq(host->irq,pxamci_irq, 0, "pxa2xx-mci", host);
' Z! L9 q( o. u" K3 V+ U# s5 r回顾一下,host->irq就是刚才从platform_device里得到的中断号,# I! L8 R$ ^! ^2 i
irq = platform_get_irq(pdev, 0);$ T( V" W% X6 B6 R) D4 x, k8 e0 L& g
host->irq = irq;0 @- x6 a9 s  {9 E3 ~, y
接下来,pxamci_irq便是为该中断host->irq申请的中断函数,) @" }6 r3 B2 e& q+ Q1 p9 K* i
static irqreturn_t pxamci_irq(int irq, void *devid)
0 e. ?6 ?- g5 F3 b3 Q{0 r7 O: x0 b: [4 x. Y/ @
struct pxamci_host *host = devid;   // 得到主控制器的数据" k+ W8 X7 y+ q) U
unsigned int ireg; int handled = 0;
+ t9 \: q  l! _" r* j ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK);  // 读取中断寄存器的值
3 g' j& O% t0 g  h$ K if (ireg) {
) w$ X1 j+ k& s( F  unsigned stat = readl(host->base + MMC_STAT);  // 读取状态寄存器的值2 E/ R8 u8 \% o- W( c2 }
  if (ireg & END_CMD_RES)
" W1 L" k9 v, U/ p; \- j; R/ b   handled |= pxamci_cmd_done(host, stat);
3 U# P& G! M' N; h. X( N  if (ireg & DATA_TRAN_DONE)/ W5 y& t  J3 a2 F  l
   handled |= pxamci_data_done(host, stat);
; O' E% D9 u+ t7 V% o$ y  if (ireg & SDIO_INT) {$ T9 r8 Q) {1 k. P5 d# N7 g# m: g
   mmc_signal_sdio_irq(host->mmc);
/ Y, P+ [: Y) w  }
. R. v0 }5 T7 `& r }# `, L8 g2 Q4 h) V6 c9 p6 u
return IRQ_RETVAL(handled);% B% D* C+ @7 F3 ]/ b: g
}0 L5 h* Y, W2 j3 @& F* {! M2 ~- E2 y
当 调用(*request),即host->ops->request(host, mrq),即上文中的pxamci_request()后,控制器与SD卡之间开始进行一次指令或数据传输,通信完毕后,主控芯片将产生一个内部中断,以 告知此次指令或数据传输完毕。这个中断的具体值将保存在一个名为MMC_I_REG的中断寄存器中以供读取,中断寄存器MMC_I_REG中相关描述如下,) F) J& l' O" d% O' V
  O/ n! H3 X$ E
如果中断寄存器MMC_I_REG中的第0位有值,则意味着数据传输完成,执行pxamci_cmd_done(host, stat);
/ s! F- {5 r, o, y# Q' s4 }; s如果中断寄存器MMC_I_REG中的第2位有值,则意味着指令传输完成,执行pxamci_data_done(host, stat);  k( a# g8 k1 v$ \5 ]' a! a+ I" f
其中stat是从状态寄存器MMC_STAT中读取的值,在代码里主要起到处理错误状态的作用。  z; f" r1 n! ]# A% I! K
-> pxamci_cmd_done收到结束指令的内部中断信号,主控制器从SD卡那里得到response,结束这次指令传输8 ^* w7 O" v# ?- S* V# R
这里需要注意,寄存器MMC_RES里已经存放了来自SD卡发送过来的response,以供读取。
: F2 _! e9 y$ G8 N6 Wstatic int pxamci_cmd_done(struct pxamci_host *host, unsigned intstat)
* M- B" o' `) P" n6 [8 e{2 U. o' \* |: |$ j
struct mmc_command *cmd= host->cmd;6 z8 ^; u) K" `5 @: D% o
cmd->resp= readl(host->base + MMC_RES) & 0xffff;( U5 F+ H! ?: F; Z, g3 `5 k
if (stat & STAT_TIME_OUT_RESPONSE) {
& Y4 z* p: n6 ?0 @, x( |+ z2 k  cmd->error = -ETIMEDOUT;
1 X3 \% D6 h, V9 b6 O$ x } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {- A1 U% w. s) j' P& h1 o
   cmd->error = -EILSEQ;
/ z" N! i$ a4 |+ R* s) ?}/ Y6 q6 \+ @+ L3 j: K
pxamci_disable_irq(host, END_CMD_RES);
3 _3 ~% h1 \# R2 U- c6 d if (host->data && !cmd->error) {+ v7 g* o5 ?, k' E* v+ M: O
  pxamci_enable_irq(host, DATA_TRAN_DONE);
$ q% t! N4 `# X8 j } else {
6 a6 `# @7 M: U1 l  pxamci_finish_request(host, host->mrq); // 结束一次传输,清空主控制器中的指令和数据* L- Z& t3 _( T$ W- ~5 O! `) P
}- j7 n: A. N3 U8 B
return 1;: S# y/ G8 w0 q" H5 y  U: d8 F
}
4 Y8 F3 H+ X7 W  O-> pxamci_data_done 收到结束数据的内部中断信号,得到传输数据的大小,结束这次数据传输
8 N* N! ?+ m1 Y1 r6 u3 \6 D! F static int pxamci_data_done(struct pxamci_host *host, unsigned intstat)2 q" m# N9 |* `2 e" S7 u
{
/ Z9 ]9 {+ r0 F2 { struct mmc_data *data = host->data;" [0 @. Z% [( C2 K
DCSR(host->dma) = 0;) v2 c9 ^9 p: a: T6 r  o$ g
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, host->dma_dir);& _- @. O+ {5 C8 G
if (stat & STAT_READ_TIME_OUT)
  v$ C/ T$ p; i' t0 D3 m- D  u$ e1 r  data->error = -ETIMEDOUT;
* t( W* t1 S& Y! c else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))* P' V" d1 N( C
  data->error = -EILSEQ;
( b$ s* `  A/ A if (!data->error)
: _* G6 I: j$ z1 e  data->bytes_xfered = data->blocks * data->blksz;  // 数据传输量=块的数量*每个块的大小(一般为512字节)
6 {, a# O- I) d9 _ else
. E9 ~# h2 G/ W  data->bytes_xfered = 0;
) ]1 p2 [' n  k% Y6 w# G: o  H pxamci_disable_irq(host, DATA_TRAN_DONE);
  s. R& N2 _: A host->data = NULL;
6 u9 `) S* s- G) T* a# X' N pxamci_finish_request(host, host->mrq);3 V4 v! ^. B6 U+ H: r0 Q1 Z; J
... ...% Y" o' i5 A6 T" k5 v' Q1 q8 {
}: z: ~6 x, m" H( H& _1 p. p
static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)5 B. Q: q3 ~; B! J9 ^
{
5 I7 E/ G& A5 s  h7 D, m+ Z- f) D host->mrq = NULL;
- s: l  z! Z8 I; }( y/ N8 Q host->cmd = NULL;0 e1 J) x, }  e, Y: J$ l
host->data = NULL;
. ^/ L" m) |' p  W7 c5 i mmc_request_done(host->mmc, mrq);
$ r/ D7 {! v2 B* ]4 F; j set_mmc_cken(host, 0);
( p! Q0 d0 `4 ]% N unset_dvfm_constraint();# Q( s. ?- {5 h2 A; W0 O
}) g$ q: i( \! s, V7 T( a4 e4 M
/* drivers/mmc/core/core.c */! Z9 s( T' G! Y2 N$ A" e5 x  t! N
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)) y6 C/ v) c- L; t
{* z% E/ n9 J2 n: n3 W6 U1 ?
   ... ...
* {  @) s, _9 z$ f6 s) Y   if (mrq->done)
. P" P- L0 J4 y; L0 h! f& i   mrq->done(mrq);. Z+ t4 l1 |3 F8 A- m
} / R# y; Z9 K# L6 s! v
这里用到了完成量completion,LDD3上是这样说的,完 成量是一个任务的轻量级机制,允许一个线程告知另一个线程工作已经完成。这里的一个线程就是内部中断处理函数,它是结束主控制器与SD卡间通信的线程,通 过mrq->done(mrq); 即complete(mrq->done_data);告知另一个线程-回调(*request)实现主控制器与SD卡进行通信的线程,通信已经完 毕。1 f5 v* x7 E2 b) L2 N0 G0 F
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)% }- M$ K* n% s# H/ J
{
$ N  G7 D  O7 ^# _ DECLARE_COMPLETION_ONSTACK(complete);
) s$ M/ [' ?9 R1 Z mrq->done_data = &complete;* q# v7 u" c5 i! t; T
mrq->done = mmc_wait_done;/ |% A& _; Z2 z9 ~
mmc_start_request(host, mrq);
( S- s# q6 U, w( i$ F wait_for_completion(&complete);
9 r1 z" `5 y4 y" o* ?% C& g}) Y% \; ~3 M: l; B# A" o% l
static void mmc_wait_done(struct mmc_request *mrq)
1 q2 w5 ^6 Y, T{
* e( ]1 S  x6 A  Q! q$ |& [ complete(mrq->done_data);
1 o8 u* b7 i& u2 L! u' O9 a}, z+ m$ d' \* f1 P0 o  x- Q' x
2) 探测引脚引起的中断' l" Z/ K" [9 F
" L; C% t: n: [6 K% M; ~& |+ o" }
  if (host->pdata && host->pdata->init)9 Y" M1 F, f/ x8 P( x
   host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);$ _% B' D" s" S$ M1 S
该init()回调函数就是刚才提到的系统初始化时通过saar_mci_init()实现的, $ {* x  B* m. @8 W2 W% U
static intsaar_mci_init(struct device *dev, irq_handler_tsaar_detect_int, void *data)
# c$ }5 B2 }7 k! S{
1 z& ~. A) w& z4 t3 [# V$ w! D request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "MMC card detect", data);
" X' _! e; a* N. A1 J& K  w, ?  B ... ...$ S5 I3 `  A7 a/ s* U8 v7 G
}. A3 Y0 N$ D& V! T) a
其中,cd_irq是通过GPIO转换得到的中断号,pxamci_detect_irq便是该中断实现的函数,之前已经提到过mmc_detect_change,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数。
; X! X2 o2 G5 Tstatic irqreturn_t pxamci_detect_irq(int irq, void *devid)% J; k1 ]0 y. J& Z: J9 z- M
{/ o& ?7 E# Y" ^* v
struct pxamci_host *host = mmc_priv(devid);
+ P+ t3 O  i7 x+ J2 h mmc_detect_change(devid, host->pdata->detect_delay);  T1 W( ~5 D/ k: L3 O( \9 |( W0 i/ g7 O
return IRQ_HANDLED;0 P( `' l+ S! |! _5 V7 X
}" _* a# O. m- {3 a9 ?& ?4 f
当有SD卡插入或拔出时,硬件主控制器芯片的探测pin脚产生外部中断,进入中断处理函数,执行工作队列里的mmc_rescan,扫描SD总线,对插入或拔出SD卡作相应的处理。下文协议层将讨论mmc_rescan()。
3 R4 }3 V! d1 G- q8 Z0 u/ F% \

; N3 ]3 @  |$ ], K  3. 协议层
7 o5 s* S2 e* K% m

* A4 k& A* x% b& Q. P6 Y   Linux在内核源码的drivers/mmc/core文件夹下为我们的提供了一系列SD卡的接口服务函数。可以查看Makefile如下,! G& H4 N3 J4 Q+ s/ y. G

9 ?% Y1 C. g, H0 ?& F5 X可见,core文件夹下有针对总线的服务bus.c,针对主控制器的服务host.c,针对SD卡的服务sd.c, sd_ops.c等等。8 D# M- ?2 e+ k  d; s* G
4 p, A5 V) P! Y- G* }
其中,最为核心的一个函数便是之前提到的位于core.c的mmc_rescan,概括来讲,主要完成两项任务,即
6 c# G& p# ?- b
- g' c# R8 e8 y+ B9 R
  • 扫描SD总线,插入SD卡
  • 扫描SD总线,拔出SD卡/ T' R9 t+ f* \4 v) _) ^6 G+ X

/ M' o  Y9 R! `3 \8 J
( [. o  E5 d: u/ {  z; X3.1 插入SD卡$ f6 b+ Z0 e. Y, b9 Q* N5 q: ~
+ M% h+ g% E4 w1 F1 W% t# F" |
插入SD卡,主控制器产生中断,进入中断处理函数,处理工作队列,执行mmc_rescan。
& A3 G) h( b- t7 c( M. k
1 Q7 i8 Y0 U# n5 _1 xvoid mmc_rescan(struct work_struct *work)
% X. {6 [; _0 X  Y) j2 U{
( s5 u, P* M% J7 Y) e struct mmc_host *host = container_of(work, struct mmc_host, detect.work); // 得到mmc_host的数据1 j; ^5 ]  r% _  s
/*
" l# U3 i; X  z/ P$ U  * First we search for SDIO.... k; c/ w( d% r$ _. B
  */8 i3 A% R1 F( w7 r6 C
err = mmc_send_io_op_cond(host, 0, &ocr);
9 f  U5 z* C3 U8 {; h if (!err) {3 G  @2 b8 O/ O2 j( s* X' z7 C
  if (mmc_attach_sdio(host, ocr))
) C- g  A: T- [5 \& w5 ~  x3 t8 M   mmc_power_off(host);
; ^  D6 R4 k9 v. a  goto out;
6 U9 o$ G/ }- z }$ e  n1 `+ X6 H. D% G; e

. e; s1 d: d& @9 l$ g* z$ k/ ?* h /*
5 A% G1 e. u; Y5 \" s  * ...then normal SD.... \1 X/ P4 h8 a* `; F8 a
  */
. r: o( E( t1 s# Z1 J7 b$ f err = mmc_send_app_op_cond(host, 0, &ocr);3 R5 v+ v* b% k8 [3 a
if (!err) {
, E+ w! W# p9 R" w  if (mmc_attach_sd(host, ocr))( d- f+ F) i. e- M6 M7 s; s: ~- ]
   mmc_power_off(host);
* f# m9 v% [5 [6 ~* u  goto out;
' z$ q5 G% n5 r9 h2 T1 D( L }- w) x6 V4 k9 Z2 z! J
' d) j4 ?2 r, F- e# x$ a
/*( j0 O( H) |' B# X4 }
  * ...and finally MMC.# ]% M: i  B0 D: b
  */
6 \6 D6 F0 H1 t% | err = mmc_send_op_cond(host, 0, &ocr);
7 {; v" k6 @" O7 M2 S1 d if (!err) {
1 u& N6 m, f1 K" n1 A8 Z  if (mmc_attach_mmc(host, ocr))
. e0 k: Q& n0 A" V0 Q, A' Z' r   mmc_power_off(host);) r; G7 }; P  E3 f. z
  goto out;5 ^) s% S5 ^6 N3 O5 r2 }0 T# F
}# H7 h" N1 a6 E1 W/ k& f

8 i* Y+ F/ `5 p) g5 Y% h2 d  ... ...
1 ]1 A7 R9 s+ a1 S0 n6 I# s7 w5 L) [& Y3 U' w1 l. B9 `
}
+ N. X9 k  ]' {- r, T. l
# {& r8 ~/ @# C插入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()的实现过程,( s' Z; M8 v+ K6 |/ r1 T6 N# u
+ {, j! w3 T1 F0 E4 Z& o, L
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
4 A  T/ y& u% O8 Z. F. k{
& i; |$ ^# n8 ?% |9 T* X1 _1 V struct mmc_command cmd;0 {7 p9 G) a5 j; e
cmd.opcode = SD_APP_OP_COND;    /* #define SD_APP_OP_COND   41   */
1 j2 w! [. o' e, m+ h  J mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);' y% B/ e9 W$ y1 L: T, ]7 D

2 n* ~0 ^( S' v" Q# z  x. X3 a/ C ... ...
" c" n: |& d4 b  e$ h6 ]% B6 x
& C4 j6 |. d* R. o}
3 m! o- U( X5 D6 [3 sint mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries)
  B+ r& X7 J: N7 Y$ e, {! |0 i{
) w+ C2 E6 T$ w( B7 ?) \8 {5 S
% Z7 A0 p  y% ^, E' U1 ?  mmc_app_cmd(host, card);   /*#define MMC_APP_CMD   55  */
; P5 k2 b0 Y- o& e# y* b) I  mrq.cmd = cmd;  [" p+ g8 v% L! h5 o
  cmd->data = NULL;9 y1 m9 o# j8 R. r( d) Y

4 m0 z# Y& `) Q" @, H  mmc_wait_for_req(host, &mrq);
8 r( O. A+ b) v" u4 r
& ?& q& Y3 Y) G' o  ... ...
/ w% g* Y- t3 b
0 d! K6 l  s5 P: c, B2 D( f}0 `9 ]# d8 \9 A3 L( e5 S

3 U! [; c" n: T这里的指令SD_APP_OP_COND只有SD2.0的协议支持,也就是说,只有普通SD卡支持,所以也只有普通SD卡能够成功上电。
0 [& R& A6 L; A0 ?1 Z' t% r4 d, t% S, h+ Y! f
如果上电成功,就开始进行总线与SD卡的绑定,通过mmc_attach_sd(),绑定过程可分为四步,
  q9 {4 P0 N8 J, o
  m# t0 {5 C. }& o注册SD总线上的操作函数 - struct mmc_bus_ops mmc_sd_ops1 n- T* C: f( a" r% ?3 v6 `$ A
设置主控制器的时钟和总线方式 - 通过回调函数host->ops->set_ios();. E7 w( j* w  [) ^& \. y4 B
启动SD卡 - 根据协议,完成SD卡启动的各个步骤
# Y. g8 N# P( d$ f, Y, u注册SD卡设备驱动

% ^' I; O5 a# b8 [0 ^8 X- k$ X5 f% T+ n- _6 E' }0 B
3.1.1 注册总线上的操作函数
, x/ D1 F" ?3 P( @$ o
+ M+ X6 K+ I# _& J3 ~, r
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
2 z% {" M) _$ B$ |) J' b8 o{
. f/ D; Y2 [/ M  z mmc_sd_attach_bus_ops(host);
1 P- {" M3 c6 Q' X* i3 ~+ X( m1 `3 Z2 }+ f# V- R
... ...% J; V( s0 C! L

  _3 P) S' B0 s$ _' f}
$ J' e5 _* V* z. _8 y0 q
6 a8 Z, d: v4 estatic void mmc_sd_attach_bus_ops(struct mmc_host *host)
. i! _. c2 R7 {' B: @. d- B{+ w- K8 V2 v, r. I- B
const struct mmc_bus_ops *bus_ops;
- k/ m2 \$ q' _7 b& o8 v
, r2 A5 }. e9 D4 z4 f bus_ops = &mmc_sd_ops;
5 g% k6 l' o9 d# i2 h5 r mmc_attach_bus(host, bus_ops);5 ], W, P5 T0 `& j0 ^
}$ x" C; r+ b$ k! q% Q
# k3 a" l0 k' {' I
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
' D. ]) p! S. N3 A3 j{/ o" p; ?: J  _. |
host->bus_ops = ops;. N% i' M6 y! j  [: @3 h
host->bus_refs = 1;
& u" A. a# S- e" d" D host->bus_dead = 0;: z# `4 O" o, g
: ]* E5 K, H4 k! E  H8 T
}
  n- m! K, x# Q2 O* B8 u. y5 T5 |( a! V1 i
static const struct mmc_bus_ops mmc_sd_ops = {) H- a# E/ N+ h* q
.remove = mmc_sd_remove,  // 拔出SD卡的操作函数# Z0 A. b5 K7 Z6 M& C6 S" Z
.detect = mmc_sd_detect,      // 探测SD卡的操作函数$ E+ Z" s/ L( e& @
.suspend = NULL,. Z% D+ f3 d7 j+ W; G( e! o. R
.resume = NULL,
) r$ f4 f: ^1 P .power_restore = mmc_sd_power_restore,  // 重新启动SD卡的操作函数+ h" P$ Z% B, P1 p" T3 u
};) ^; D' r: s; z
这里的mmc_sd_detect和mmc_sd_remove就是拔出SD卡所需要用到的函数,下文将详细讨论。这里需要注意的是,插入SD卡的时候, 并不执行mmc_sd_detect和mmc_sd_remove这两个函数,但是会注册它们,也就是说,这两个函数的功能已经实现,将来可以使用。
0 s, G! k% w& b, Q7 K( M, I& i. J
- y) P6 Y- a6 a* X& A; V3.1.2 设置时钟和总线" @6 L' E0 [! m! g
. L1 w2 ]3 X  x2 {2 c, o- S8 ?5 Z
int mmc_attach_sd(struct mmc_host *host, u32 ocr)4 n# |8 w8 e9 j! K0 ]
{
: w5 _5 S4 `" D3 K" Q/ w# q/ @  host->ocr = mmc_select_voltage(host, ocr);
' D- ?+ S4 e0 S1 W0 K) S
( z9 e# L" s7 f  e  ... ...
( V5 N. B" g; q0 y1 U( y
! |# b+ B, n' H9 ?0 F4 A. B: h}3 {# x0 _" s  ^9 L8 g: T# i

3 y5 n" L  Z2 xu32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
) k4 X1 q- U4 e# O) k: V{6 Q7 B2 X! A: Q" f3 y9 D2 ^3 ?
  mmc_set_ios(host);4 B- @0 J% }& x7 u2 l" N& b1 Z  J" q
* Z/ j/ q4 g* B1 F; f
  ... ...- f9 x! @4 U. \# u: x8 K
}
- c5 E. \2 q2 [- U4 M, \# U$ kstatic inline void mmc_set_ios(struct mmc_host *host)
0 ?9 u- q, `% Y{9 |+ d6 H' H3 F" }2 h4 u
struct mmc_ios *ios = &host->ios;$ d$ v& }  ?4 M, C# j

" A/ k1 A' ~$ l+ e host->ops->set_ios(host, ios);  // 设置主控制器时钟和总线的回调函数,具体实现由主控制器驱动完成& o) w* x) {2 u- h% ]
}4 a$ ^, v8 d' a& @9 l3 s3 R
从这里可以体会到回调函数的精髓:协议层里利用回调函数为所有满足该协议的设备提供统一的接 口,而具体实现由底层不同的设备驱动各自完成。注意到,之所以要定义一些放之四海而皆准的公用的类,比如struct mmc_host,就是需要通过struct mmc_host *host指针作为形参传到协议层所提供的接口函数中,从而得以调用。
! K& e; F' f, z( W, b* g$ o+ O0 Q0 n( v) D
3.1.3 启动SD卡! y/ f( ^- q; f3 J2 h

! O# G) I' |7 e* ^, ^/ u( q" x" @ int mmc_attach_sd(struct mmc_host *host, u32 ocr)* ^% F6 \/ c. y5 R" z
{/ A, I; w5 J: I2 o1 i8 \4 g4 [

( ]' ?8 x+ ]0 i) f$ q& l  mmc_sd_init_card(host, host->ocr, NULL);$ e; h' \+ K# t, P0 m
* w8 x$ _+ y6 H! Y% w3 a0 r0 w
  ... ...
& l: e$ O6 O2 M* Y  ?8 `: }4 ]4 i5 ?1 w1 H  ]. x5 e) f
}
* Q" a% S+ {" b$ B
5 d* b; X/ Q; `/ [4 h* v1 {  mmc_sd_init_card主要完成以下任务,
# j+ F) j/ Y. a
0 L. }6 N8 Q& y2 Y2 f
  • SD卡的启动过程
  • 得到寄存器CID, CSD, SCR, RCA的数据
  • 其他操作比如切换到高速模式,初始化card
    9 s+ C" e# u; D
4 s  v- h3 g! _- {2 ?: x% I
  D, O+ R" I. y1 ]0 j# ?! c
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)
0 q/ P, |" r& m3 ^6 ~; j{
8 m7 b- _, Q5 ?: v. }' i: E' E
9 N( J. t: m: ~  /* SD卡的启动过程 */" ^' R) f( z% \8 o# d# _

9 \0 F& Y( p- d" k" l( H  mmc_go_idle(host);
- c% S) J1 U& w, x8 c- E/ H
7 o. S  A- W/ U% h  mmc_send_if_cond(host, ocr);
% @5 f) S/ n% B" ^/ m0 P. h
2 u. h6 B, [2 k; y- g8 ?. D. l$ H  mmc_send_app_op_cond(host, ocr, NULL);
- _1 a4 X' z1 s* j
6 C/ e4 X# x8 x6 p5 N  mmc_all_send_cid(host, cid);3 |6 l/ S7 t$ K

; a5 D+ ^7 \5 P7 A  D2 y2 w  mmc_send_relative_addr(host, &card->rca);
9 p7 D6 D% `" ^+ K7 e9 A
2 s/ [1 g* l2 b( j& E7 e4 U0 t  /* 得到寄存器CID, CSD, SCR的数据 */$ S$ z  p4 k; G; n" ~
* n: a5 ~/ b* V" b8 k9 c; z# g% P
  mmc_send_csd(card, card->raw_csd);& I3 \# n+ T5 @' e
, q1 _+ }3 u" o1 f1 }# A; Q; i
  mmc_decode_csd(card);6 W# `# a5 P; j0 e7 G: N

( m" w* i* W! x8 s3 ^0 V& c* O  mmc_decode_cid(card);9 V2 ^4 o$ T- j+ r( L0 y' f

0 w: }2 e% M. j& c  mmc_app_send_scr(card, card->raw_scr);
  x5 K: f% B8 ^; h% Y7 _) O) z
1 E) T" j7 Z% ^( o( m  mmc_decode_scr(card);
& T  V9 r- g/ |# e; ^+ E5 \% C" e5 N! i9 o  T: Z# N
  /* 其它操作 */2 I7 u/ ]: s. ?" [
: b% I3 Y' D2 U) S6 v- k( u
  mmc_alloc_card(host, &sd_type);
3 b/ L" ^! ^) G2 X+ {3 P9 p2 V( ~/ Y% q9 l
  mmc_select_card(card);
9 k- T$ O1 \% M# q5 ^
+ U1 S! T& _1 P/ h4 m+ M! H" r. Y  mmc_read_switch(card);
. S7 I/ Q; O/ W9 t. q! ]0 y  j" z  N" ]% U
  mmc_switch_hs(card);
6 U/ h5 a. P; L; i8 {5 |% f$ T) R, e4 ]2 D! m& L+ ]5 M
  ... ...
' ~  U+ z, V( O1 t) J$ g+ _4 d/ m
4 F7 A6 N  n4 Y+ a: |( m, Z+ Q}: a( _: j" o" k. U/ @

2 _, y( G1 v; X8 b1) SD卡的启动过程" O! p0 P5 M* ^& b

; M. ?! k5 Y$ S. L$ \* i/ b  根据SD2.0协议,SD卡的状态可分为两种模式:卡识别模式(card-identification mode)和数据传输模式(data-transfer mode)。这里,我们关注启动SD卡的卡识别模式。, }9 p7 l4 k, q& K+ `
- |; S' y: S/ C. t/ L, v3 f3 B: Q

9 r+ [5 K5 e" N$ b3 @7 g" ^
( R$ c) J1 i* M- i& P  o结合代码,& V( J+ |9 m( `8 S

, e0 `9 y! K( J- r, }4 ?' M  mmc_go_idle(host);                     CMD07 X% @0 s8 E$ i2 I! Z0 R

0 i7 u2 B* f: l; }' P  Idle State
* C. \5 X% @& X- N; X9 B2 z$ j% c" H1 E$ Q' c2 r2 w% h
  mmc_send_if_cond(host, ocr);     CMD8
( \7 ~' A$ `$ p( Z- N. h* S( Z- }
' m& i7 l! E# J6 Q5 b: U: }. s8 }  mmc_send_app_op_cond(host, ocr, NULL);       ACMD41
. @; p) V2 k! A- F$ U8 I
9 y" q* W$ P; `7 F  Ready State9 G: H. M6 K9 M  \

7 G9 G/ \4 w# h$ |# p) Q  mmc_all_send_cid(host, cid);       CMD2& l( a* z% [) Y

0 I) B( e- J9 `  IdentificationState
# C  @, e# M2 E8 V' \
% d8 S" q/ Z2 f7 {- C) T  b  mmc_send_relative_addr(host, &card->rca);     CMD34 }  ?* @2 w4 V2 y: f0 W# m9 n

/ v/ j+ M3 q+ A! X) P  Stand-byState0 h# P. D5 a& L; o0 r6 ]! \

, Y" g- [. p, R) }5 N* N. W' F2) 寄存器CID, CSD, SCR, RCA
$ ^4 w8 \& |3 i; x  e
. u* z, r* a* ~" w-> 发送指令并得到寄存器的值
! |9 r% a4 a2 v$ a; `4 q, q. k5 L- ]( Q  y+ I$ s0 G
  当主控制器向SD卡发送cmd指令,比如mmc_send_cid(card, card->raw_cid),请求得到SD卡CID寄存器的值,当主控制器发送cmd完成后,芯片产生一个内部中断,处理结束 cmd的中断函数,之后得到来自SD卡的response,即CID寄存器的值,存放于host->cmd->resp中。关于内部中 断处理,参看上文的中断一节里的 mmc_wait_for_cmd()。" X, Z( O. a  L/ L) ~

* F5 C1 v5 Y0 ]1 i, J8 b! \7 hmmc_send_cid(card, card->raw_cid);这个函数发送了接收CSD寄存器的请求,并且得到了来自SD卡的CSD寄存器的值。
! v& R) {; ~0 a! _" Z' \6 q
2 G3 A8 Y- D/ ~5 _8 M/ P3 T5 N1 Jint mmc_send_cid(struct mmc_card *card, u32 *cid)
8 b% W, T( U0 {0 w  Q{
( u" X  e. X( K; a- i8 v: f   return mmc_send_cxd_native(card->host, card->rca << 16, cid, MMC_SEND_CID);
& r7 n3 u1 N( @7 T# ]# H# W
; ]5 P; E/ E$ b}+ Y# D( D2 _6 |$ [9 ~3 }7 B

3 K( q; t1 ?3 ]- K- a1 }0 K7 A+ vstatic int mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, intopcode)
) i, |; Z% I9 u; e) L% ^! s( T% r. X{: y. q, u6 s  E( H
cmd.opcode = opcode;( }3 P2 N4 y5 N( D
cmd.arg = arg;
: n, J+ C. Q* D1 z% ]5 G, A cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
) n9 I% Q0 W0 B; C3 Q0 Z" {- I. L" B' n6 B3 R
mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
9 h9 n8 {  X8 ^* d. i- `  I, n& O" ]* X' L. j: u+ l! h
mEMCpy(cxd, cmd.resp, sizeof(u32) * 4);  // 得到response赋给cxd,即card->raw_cid
- d1 h) Y$ D7 x' M6 S# Y0 T( I+ l: _- U  m- G/ w0 R" K
... ...
, \6 B% m/ h! D}
) @) W+ R: `4 R4 Z( m4 Z2 @' T/ c( x& _* P
-> 解析寄存器的值% w; t( i: ]' c

# G1 i  R% r% v! i为什么要解析?先来看看寄存器CID在SD卡协议里的定义,它是一个128位的寄存器,存放了关 于这块SD卡的基本信息,就像自己的身份证。通过mmc_send_cid()将这个寄存器的数值赋给了card->raw_cid (定义 u32 raw_cid[4];) ,为了方便得到具体某一个信息,协议层为我们解析了寄存器里的域,并赋给card->cid,比如厂商名称,就 可以通过card->cid.manfid直接读取到。9 z9 F* ^0 y" y  r* h0 b3 z8 {

9 D$ Z" W. [' U+ ]; j4 j0 K( F, G0 D3 a5 F6 W

8 K' J- h0 X, N* estatic int mmc_decode_cid(struct mmc_card *card)
3 n/ u+ R$ j. ], r{
0 r; y9 \: n9 F/ |1 w) S: a  u32 *resp = card->raw_cid;! K' ]" M: C. i& N! s2 Y  k2 a
4 B, `2 k( B) R
  card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);; N7 i9 s* a1 t, o( O  z
  card->cid.oemid  = UNSTUFF_BITS(resp, 104, 16);. y- ^" C$ e% C" p
  card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);6 A; i" g2 m- y+ X
  card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
& D- c5 P$ f4 |9 r* F- W+ n/ ^, Z6 B  card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);2 ^( Q# j1 n* V, a% b# N: m
  card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);- ^' ^8 |3 O4 c- w! ]
  card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
" s7 w  N; w- [* T* ~  card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
9 |9 c. r- ^" x0 ~9 k& l  card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
1 _; d8 ^& Z0 H. h* Q; n- v+ u9 ^+ q8 F  card->cid.month  = UNSTUFF_BITS(resp, 12, 4);
8 t, C0 L& M' u0 d  card->cid.year  = UNSTUFF_BITS(resp, 8, 4) + 1997;  K' Z7 M& k4 [+ s" j
  return 0;0 D% C2 B/ c% B: X& {8 C
}  v2 {+ u  l$ G' ]5 [- m" Z
2 z$ m2 |- I' E/ s8 }
3.1.4 注册SD卡设备驱动
! I7 Z  I& l2 \5 u
4 A) Q  ~0 d( P2 v int mmc_attach_sd(struct mmc_host *host, u32 ocr)
' m% I5 [& ~, T{
# D+ `( @) y( \8 c- M
. x! Z  D$ Q: ^& h- C  /* mmc_alloc_card(host, &sd_type); 在mmc_sd_init_card()已完成 */8 S, `+ o# d3 [: R; t) I' O& r

# q% M3 y  }9 n& U0 z  mmc_add_card(host->card);1 M, \4 k4 D5 Q" h1 @. I# q1 v! d

/ d: D( B' R4 b% C+ k  ... ...$ q& V# D; e5 d. C  j/ S8 t

1 C% `1 |- L& W# ?; n( j) r* d}8 q, i3 G$ v  C9 Y) C/ F9 E) @
' p; M9 g1 Z% \) [6 H3 p5 v
上文已经提到,设备驱动程序都会通过alloc_xxx()和add_xxx()两步来注册驱 动,其实质是调用/drivers/base/core.c里的device_initialize()和 device_add(),device_add()完成建立kobject,sys文件,发送uevent,等工作。. H3 Z! C) r& ]8 m, C8 U
+ F7 P) S) S% A4 W6 ]7 X
3.2 拔出SD卡/ O  b$ U' q2 x) l5 T1 u+ ?
6 u3 M3 G& v- Q, t4 J
void mmc_rescan(struct work_struct *work)3 w* y; K6 {3 L/ u4 Q. c* @
{$ V+ }* }" i1 K/ y: n% h, X
struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
% Z& o: {0 J: s mmc_bus_get(host);
: O* Q* C% x$ V0 F6 K' s
1 z1 n: B  p! ?: [; N- F /* if there is a card registered, check whether it is still present */5 ~* N& K* I% W- g5 ?
if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
  ~9 k; H& U/ m) E. ?) E  host->bus_ops->detect(host);
3 l/ {! \$ @! m! F* j% a& z2 _2 {/ k$ F$ Y- A6 k5 ~
mmc_bus_put(host);
* b% x$ T) J% R/ G8 C- I* d( y5 N; q9 J' M7 G% V4 _
... ...) r& d  R, S$ {0 C$ _
. E9 f& u: M5 E% s4 S3 ~* @
}( _  ^. Y- \  k3 t

7 p  c0 q8 m5 a这里的mmc_bus_get/put(),为SD总线加上一个自旋锁,规定同时只能有一个线程在SD总线上操作。2 Z) B1 ~- g( P! O) c! ^4 n

9 P; ~9 Z! {, {; t7 p3.2.1 bus_ops->detect()
' T- P5 L9 t% X6 i* t6 g" e( S2 }9 D8 v" S! G: H* B4 n
mmc_rescan()扫描SD总线,如果发现host->ops上赋了值,即之前已有 SD卡注册过,就执行bus_ops->detect()操作去探测SD总线上是否还存在SD卡,如果不存在了,就执行 bus_ops->remove()拔出SD卡。之前已经提到,这个bus_ops->detect()已在mmc_attach_sd() 注册完成了。
& X& Y$ }  Q0 x  U
+ V* H9 i1 m4 m- i2 i static void mmc_sd_detect(struct mmc_host *host): }* M! E* Z" D& o2 ~1 G! B
{
' V! W5 M. ?* L mmc_claim_host(host);
; I5 N4 `) ^! k7 V: b( o$ A" v" v: m# c$ c1 [
/*
: c# w" b/ ]$ L& o1 |2 o- E- ?7 x- P9 u  * Just check if our card has been removed., H* n# z5 |: Z9 g7 I" O/ q
  */
( s  g& B1 L% ^6 a/ ? err = mmc_send_status(host->card, NULL);3 c6 U+ T" m* r: r& I8 Y' {3 u: |. T
* c" r, v8 x9 L8 I9 h; k. x- ~) R! J( g
mmc_release_host(host);# y8 Y1 S  B/ X6 U. f) |$ N

8 H# `6 \; W* ?( z. Y* g+ l if (err) {
3 u/ g/ c# J# S: {; v  mmc_sd_remove(host);2 K& w8 P- ]' K: l* v9 L7 ^

" l. W5 m% A% v, ~# `  mmc_claim_host(host);
# b  d6 l" r) W! A. ~  mmc_detach_bus(host);
# A  `6 ^* w9 u5 _+ ~  F  mmc_release_host(host);2 \2 p  X0 z5 f4 x% v* {
}; `: H4 S  T, E
}
7 e. B, v, z/ }/ ]  [$ e/ r' D这里的mmc_claim_host(host)通过set_current_state(TASK_RUNNING);将当前进程设置为正在运行进程。
8 N. X6 C1 l1 x7 E; f
$ c0 i+ \/ b/ |$ G. U$ Ammc_send_status()发送得到SD卡状态的请求,如果未能得到状态数据,则执行mmc_sd_remove(host)拔出SD卡。
0 ]1 f. Y3 y6 H& `5 K# K; N+ @+ A
+ M( Z( E4 l3 r4 ?9 E* Cint mmc_send_status(struct mmc_card *card, u32 *status)- g$ d& x/ @7 m: }
{( d1 _) v3 }. Q
struct mmc_command cmd;
+ i4 Y0 r2 X7 `& G) F$ Y, _5 Q! F; h: v: B9 q' ]
cmd.opcode = MMC_SEND_STATUS;    /* #define MMC_SEND_STATUS   13 */4 k2 E! G$ s0 K
cmd.arg = card->rca << 16;! y1 |: K5 E, j7 I1 z1 u( `) F
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
, s3 H3 S& f% i9 P  N, u/ q. b7 S3 |) ^
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
% Y- j' Y0 u- Q; ^' _. |& \/ Q0 l2 b
if (err)               
; i+ _* Q2 z# v! L( B  return err;           // 接收来自SD卡的response失败,即没有发现SD卡
- f. k6 a- G) Z) M' ?( ] if (status)4 u; ~; Z  R2 V% M' n3 X
  *status = cmd.resp[0];  }) c6 ^3 q4 m$ e% K

3 \. U; n& C" k& B2 U return 0;
) q3 r+ w8 \2 ^0 y+ Q2 K1 n
7 V: L6 h( S! r}
1 Y% S/ ^/ {& {+ c& D6 [& a主控制器发送命令CMD13,要求得到SD卡的状态寄存器CSR和SSR。
3 k& l5 q- D# o3 h9 A6 R; e3 s* t9 A1 l$ w# ^
SD协议规定,状态寄存器CSR是必须的,这个32位寄存器作为R1的一个域返回给主控制器,% \" M7 J* x  B# d% U4 U
! V. l8 e; a" a# ]
状态寄存器SSR作为扩充功能,具体参考SD2.0协议。
4 [% ?) t1 x* i6 c9 N4 P/ H4 O1 Z: ?
2 Z; S3 U- M; h3 p+ |$ M; S  A6 P# D  H4 n. g
3.2.2 bus_ops->remove()
) Y: ^$ \5 N3 [! |
. s( |  t4 U! w7 a
拔出SD卡,其实就是注册SD卡驱动的反操作,实质就是执行device_del()和device_put()。4 \' t! U. M) l( V) a! S. ^8 u
) F3 |1 j. [5 r- K4 r
static void mmc_sd_remove(struct mmc_host *host)
- G0 ?) d& z: d{9 q1 B$ v- L) l' C& A' W
mmc_remove_card(host->card);
: T# S- E* S" r3 F( Q: g host->card = NULL;5 u6 T# h$ G" F9 t, [+ a. @0 a
}- F6 }( X* f: O# _: K# @0 {
void mmc_remove_card(struct mmc_card *card)
; d# _) P5 h. P: W# d# E& U{
3 w& o3 N- X7 c% e: H1 P if (mmc_card_present(card))
: y* I& F5 f3 N% p  I) J6 v  device_del(&card->dev);  M) S& x: s. ~+ I% q* Q

" [" H0 |1 i! G5 u, j7 e# d put_device(&card->dev);
' [5 }0 O1 H: V  V}- ?& |3 W# n+ S5 U" m( t

) L- f/ y7 \8 U9 e4. 块设备' s8 p4 u/ }7 [, }  L/ ]
+ y4 J  v  ], R
首先,必须知道为什么要用到块设备。在linux下,SD卡通过block块的方式(以512字节为最小单位)进行数据传输,它必须遵从块设备架构。在linux块设备层,I/O调度者通过请求队列机制负责对块数据的处理。
% X- h( _5 f1 `3 V
0 X; a8 Y. C  P6 b0 p
9 r# p2 h/ h1 M- f
2 p0 e% d/ w6 }3 i, |; D; c/ YSD卡子系统分为三层,主设备层,协议层和块设备层。块设备驱动位于/drivers/mmc/card/block.c,主要完成两个任务,
& d) t! n3 R$ g- y9 x4 D# s( l3 n/ ]& Q
  • 建立设备节点
  • 通过请求队列机制进行数据传输
    5 }9 Q0 E3 y) {: [" q
( S9 i: I. G1 ^/ O6 a  O6 X
0 I3 H4 [% R9 d; s. {$ M
插入SD卡,注册驱动成功,那么在开发板的目录/dev/block下会出现SD卡的设备节点。
, I, [# R7 }. p0 I6 ^& A; n" d; m5 m
179为主设备号,定义于include/linux/major.h  #define MMC_BLOCK_MAJOR  1799 B, T- ]3 A. b4 W* R& z

0 L% M; c! t2 P( F6 n9 k: ~  W; C179: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)。 + i# O! {0 d. o

5 c: N% @" t3 O" M7 U% P# \下面通过对块设备驱动block.c的分析,看看SD卡是如何在块设备层建立节点和传输数据的。2 _/ ~9 I1 U8 A# F, ]  ]2 L

' u" J7 y- w+ t; g$ x) K4.1 数据结构
6 R. v: M+ |0 S& \9 K  Q5 ]: Z; t( m+ Y# ~
每个驱动都会有一个数据结构。幸运的是,我们SD卡块设备驱动的数据结构相对简单,在mmc_blk_data里,主要有两个成员,struct gendisk *disk和struct mmc_queue queue。4 h- S1 W/ K+ H" V5 K
5 T6 e1 ]/ o9 @. `# @
1) struct gendisk 是general disk的缩写,代表个通用的块设备,其中包括块设备的主分区结构struct hd_struct part0, 块设备的行为函数struct block_device_operations *fops,以及请求队列struct request_queue *queue等。! P, D: [9 U" O5 B/ x6 e1 l# l
4 d3 q7 f; c( V" r2 [
2) struct request_queue 存放所有I/O调度的算法。& n* U3 c! S$ s) m# F
( w4 h3 E. T! E& ~1 p  Z5 j+ q1 c
3) struct request 请求是I/O调度者调度的对象,其中的结构struct bio是整个请求队列的核心,具体内容请参看LDD3。* I4 p6 c% y% E3 X; [

& z$ T! I- E8 H# R& W# ?4.2 块设备驱动3 e' a" c: S# c5 L4 X

' p& ~5 V' {8 z* y( m" G首先浏览一下源码,
3 K# r; k' L$ B( M9 e' i9 m5 I: W! ?9 h8 b6 g7 \" j9 c5 m
static int __init mmc_blk_init(void), S+ ]& S0 o) ~" q, M& f, b
{3 A( `$ `& @/ z; W
register_blkdev(MMC_BLOCK_MAJOR, "mmc");    // 注册主设备号(若注册成功,/proc/devices的块设备下会出现mmc)
. `) [  B, G7 P) w! I4 f7 Z mmc_register_driver(&mmc_driver);
) q( r: A: ~3 g return 0;
% e6 F, j7 j$ E0 e2 W5 ~}4 Z4 O( U) [, V6 g

+ o% ^/ Q! t, [* I7 C4 Qstatic struct mmc_driver mmc_driver = {
6 h3 Y% c+ S: f0 \! [" o* R .drv  = {: [0 z  j3 l. c% F
  .name = "mmcblk",( Y: X" o; _3 D  Z* D! c
},
9 _1 j1 \- Z; X9 }' w .probe  = mmc_blk_probe,
9 z3 c7 E5 _, M: X7 M .remove  = mmc_blk_remove,9 f! w, w* t4 |" j  G% V9 a' N
.suspend = mmc_blk_suspend,. w$ j9 h$ X* r4 f6 q3 _
.resume  = mmc_blk_resume,
: i- n  j; r  u2 @/ N  q3 V};
8 w" Y0 N  ?. M( L# ~% v: |3 H. N& \% f1 c. [  S
static int mmc_blk_probe(struct mmc_card *card)
% W/ v1 R! C4 i9 A4 ^1 R6 [{
5 w4 X8 ~6 F! c0 X& e) n struct mmc_blk_data *md;% Y& K, a( H) _, z+ q/ w3 w9 M
md = mmc_blk_alloc(card);7 ?- Z5 Z( A. R0 X5 p' r
mmc_blk_set_blksize(md, card);
, P8 P6 I# a& Z+ M% Z" e4 B8 \4 n( v6 x6 P( }& m
mmc_set_drvdata(card, md);3 f9 ^4 o; c1 W0 F' ]( J  m( Y, I9 C
add_disk(md->disk);
/ H" q1 F: |6 m3 _+ { return 0;
5 U  E. i( X8 T) H# ]" @$ y/ C& B; M" W+ k3 P
... ...9 {; {+ r4 g1 \; \

' |% V5 z2 Q& k0 S" \& b4 t}
1 g& S/ [! J1 I  W/ R" }& {: B2 Y$ y
0 R* d2 v! \" T, \+ d4.2.1 设备驱动的初始化函数
% v: v1 K! m3 m/ l7 l8 @) y; o  x. U# w+ z# o7 m$ h( [
  仍然可以将驱动程序的初始化mmc_blk_probe(struct mmc_card *card)归纳为以下内容,+ X, e" u% C. u9 {" e* \8 p
! O% I' D" Q+ ^0 g" y* y
初始化设备驱动的数据结构mmc_blk_data,并挂载到card->dev.driver_data' m2 ]- s5 W8 q$ n3 A# Q
实现块备驱动的功能函数struct block_device_operations *fops% T) V3 m) r7 L, _
注册设备,即注册kobject,建立sys文件,发送uevent等$ b% A' S- Q9 ]# q: o
其他需求,如mmc_blk_set_blksize(md, card);
& S6 w; c' Y- ]8 x1) 初始化mmc_blk_data
+ o  c' O$ h4 ^8 ]6 ~. @0 ]. ^; U. k# u& k
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)  n2 ]6 o, A) f
{1 H7 \2 z3 F! t" W" M' [9 J. q
struct mmc_blk_data *md;
! i8 d# h' t, O/ G) r; ~ md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
; ^+ t2 S2 u  G2 F2 M$ K/ F7 y
5 @9 W% U* p% M  {, i+ q) k md->read_only = mmc_blk_readonly(card);4 H+ W8 E6 t, T/ J  y9 l

: J( F/ K0 q  j0 V9 r) b, Q md->disk = alloc_disk(1 << 3);   // 分配了8个可用设备0 E2 W6 Y, s0 K4 j- Z1 |

5 O; M9 s  J, C5 B9 y# j9 q spin_lock_init(&md->lock);% j# ]6 Q! g# N1 K: j
md->usage = 1;
) _( a$ D) d- `/ B8 v7 N# i ret = mmc_init_queue(&md->queue, card, &md->lock);
! }' h& d, f$ V% `6 k! @) W, Q  x; Z( j0 l
md->queue.issue_fn = mmc_blk_issue_rq;4 i+ v) G% l0 W# g! {3 q8 O  L9 {- G1 s
md->queue.data = md;6 o1 Y# |* ~6 n9 X- _
1 p* }! K  H% Z' L- F3 d# j  x
md->disk->major = MMC_BLOCK_MAJOR;6 w& I9 F  ]* M( v
md->disk->first_minor = devidx << MMC_SHIFT;
1 I; B' `6 W  y. `" A& ~. j md->disk->fops = &mmc_bdops;
. k" I0 n: A5 d2 D md->disk->private_data = md;: v9 v3 \* D9 x, N. k
md->disk->queue = md->queue.queue;9 j9 X: Q2 T1 W5 H
md->disk->driveRFs_dev = &card->dev;
4 O: O% S$ n7 f% e* ~8 ^5 E3 ?7 k3 [# N2 h- ?5 S9 v' r0 u
blk_queue_logical_block_size(md->queue.queue, 512);( k0 l5 P0 V9 O& D: V

# C6 Z& S* T+ q, o8 i ... ...
% Y$ K6 A0 i1 A: c
3 H9 U: |: M3 b0 ]. z+ ?7 T0 F$ K return md;, G; V% q' W" Y/ |- q6 b0 I

% \5 T3 j# K3 s$ M) Z}
7 A! K; |& L, @2 @1 n* w4 `4 p* x7 b* r8 q
完成初始化后,通过mmc_set_drvdata(card, md);将数据挂载到card->dev.driver_data下。6 ?2 O* Y* n' A

# d8 _& h. Q( b2) 功能函数+ s; q7 j7 N8 u2 v9 c
) M/ c1 V( ~; ^. s4 {
static const struct block_device_operations mmc_bdops = {. R2 l+ v% y+ D/ o
.open   = mmc_blk_open,! C2 T* ~6 g+ ?0 c+ S* r
.release  = mmc_blk_release,
7 e- Z# u6 W: ^8 s .getgeo   = mmc_blk_getgeo,
7 W" _/ b. ?) Y7 Z0 U .owner   = THIS_MODULE,2 o3 ^/ T" x- B
};
9 O4 |- t( Q3 V' j% f
0 ^0 K: _% A2 s6 G6 j# p7 B- dstatic int mmc_blk_open(struct block_device *bdev, fmode_t mode)/ @- S2 Y  t( F. E  B2 O1 l* E9 H
{- K9 a8 _. W/ d7 B( G+ z' n) W( a* t
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);  Z% Y5 l3 O/ V' G3 R

  o4 t% K9 D4 r" q) F ... ...- `; Q/ f: R- A" ~  r
}) x1 o- e# d# x5 x& w
struct block_device {9 l2 [* n* ^/ \# {' k- p0 R
dev_t   bd_dev;  /* it's a search key */
, ]3 B0 {8 E. C, { struct inode *  bd_inode; /* will die */+ T4 w1 R+ r3 ^2 T0 a3 w
struct super_block * bd_super;3 ]% P( a' m2 T. m
2 F% |& ?" g2 T/ A0 }0 z$ e
... ...1 ?4 o' b+ L* k( n. \! u  [
};8 b& T, U! Q& m" O3 k

) c( }, J% M( w2 @2 L' B与字符驱动类似,通过dev_t和inode找到设备。0 t+ j6 _# Q0 w% a9 @. i
8 E$ m) ?+ l5 V1 ]  H
3) 注册驱动  t4 M3 D" a7 K4 n( o3 E% s

0 V$ h" j$ u9 Y5 Z# |- M0 x% _void add_disk(struct gendisk *disk)0 m$ t; L) n0 M! ?
{6 e- I+ i' L+ X( e
blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk);* p5 ^  [0 N8 l. T- m
register_disk(disk);
% G% X# X3 O: g7 |* a blk_register_queue(disk);
; T8 w0 G) o: D$ F  T: \! ]. I5 [' {
1 W. z8 h7 W8 W- `7 \ ... ...
! |, m0 d% @. C3 }6 o5 V' E$ _+ d& |8 ]) K" {4 K! v8 ^$ H6 ~! t
}
6 ?' \& a" o3 {# s/ @- l
- F- O; u' h& Pblk_register_region()在linux中实现了一种利用哈希表管理设备号的机制。
, S  p% u3 d8 X- U& }( V* L6 p0 f$ b4 L) C! W# a
register_disk()对应alloc_disk(),完成对块设备的注册,其实质是通过register_disk()->blkdev_get()->__blkdev_get()->rescan_partitions()->add_partitions()添加分区,建立设备节点。, W' ?! b  U/ S% Z- @/ G$ x1 U% |8 ^
) z+ h/ Z- R* g3 u
blk_register_queue()对应blk_init_queue()完成对请求队列的注册,其实质是通过elv_register_queue()注册请求队列的算法。! X  D5 e' M1 P# `# E

1 O. ?2 Y8 l$ h% p& |2 _/ @关于块设备更为具体的代码分析可参看linux那些事。& i: @9 B7 A* V+ r: F5 t

) x! J2 r6 y1 W& ]5 Q+ U# K; W4.2.2 请求队列" q2 e& b, e+ B, p8 v; `4 b

" d7 p* }6 {) K' Lmmc_init_queue申请并初始化一个请求队列,开启负责处理这个请求队列的守护进程。2 h4 t& z6 ]/ d$ f: O

3 V( [2 M; d- I1 u" ]int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock)
3 ~1 C% d" O' e0 s+ J{
+ f/ u8 R* Q6 ]- ?! O! W* y struct mmc_host *host = card->host;
$ x9 n, H* P1 w" c mq->card = card;4 p+ p" M# `* o
mq->queue = blk_init_queue(mmc_request, lock);
- H' ~4 c6 n1 a* ^+ i" {
* H0 Q9 A1 [2 M& w3 ^6 F7 ^ mq->queue->queuedata = mq;
8 |9 n* [" N) ]- w8 g& G mq->req = NULL;
) E9 ]* K$ K/ `$ _6 y* M8 ?
8 h0 ]3 ^, ], R; j1 _ blk_queue_prep_rq(mq->queue, mmc_prep_request);  // 注册mmc_prep_request算法- i7 ~+ l( D) z) r0 G
blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL);  //注册ordered算法7 n1 T9 ~* d1 p7 l6 I3 V
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");3 Q7 S/ i* q/ F* M) ^" C- \  \

: e9 w) b% N7 }+ m   ... ...' P1 z) ]9 f9 T# f
}: \1 f& z2 p# ^4 W) v
1 c6 j* M9 ~- k& j% A8 u6 |
1) mmc_request- Y8 V- a3 R: j  {. i( p8 r  H

% D( Y) P# i& _3 M1 n2 T它是处理SD卡通用的申请请求的回调函数,或者说是SD卡申请请求的算法。当CPU处于not busy状态,会寻找一个请求,并试图执行它。
4 ]- n7 p$ O# R/ J2 |' q; J6 v, W1 F" U, p: x# A
/* /drivers/mmc/card/queue.c  */
$ e5 x7 B- g0 U5 J
3 c: z2 g  q) P4 U- A& z2 S/ ~/*
% i; H3 p1 u/ ~ * Generic MMC request handler.  This is called for any queue on a; d6 H1 ?; _6 g
* particular host.  When the host is not busy, we look for a request2 g. K' g, p$ V
* on any queue on this host, and attempt to issue it.  This may; ]/ v+ ~7 Q  g- |- f  \9 {
* not be the queue we were asked to process.
$ T2 {( J2 ]% y( h */3 Q/ R. G! N- B4 [. U6 L1 Y
static void mmc_request(struct request_queue *q)% z# c- p# U# w" o& r
{
- }. k7 P% o; O4 j. \5 j$ l2 H struct mmc_queue *mq = q->queuedata;0 _6 D5 V+ h5 X% t- @
struct request *req;  A; B9 Y1 _1 f( u; }+ U9 [
7 g6 U% N0 z% e% j- [  g
if (!mq) {1 Q' X7 K/ v- I! u7 s
  while ((req = blk_fetch_request(q)) != NULL) { // 寻找来自请求队列的一个请求req
! a# Y# P8 {5 C" ^  t' H+ z2 E& u   req->cmd_flags |= REQ_QUIET;
& g: K! b6 }) N   __blk_end_request_all(req, -EIO);
) ^; m* [$ S% ^; g1 \/ c( p2 W  }$ d3 F  f# n' I! Q1 Q
  return;
, M( N& e0 ~! }, n: \ }" h9 o" j$ x" C: ~" B% o  T

- ?9 `4 l8 r. V% M+ t if (!mq->req)
4 p# q3 \  G9 J% V6 X4 v* n  wake_up_process(mq->thread);    //  如果队列里没有请求req,唤醒守护进程
; b: j( P" C4 c  x; B, A}
& E6 @' i7 t% f# H2 l% g! i( T9 S) J5 N" l! v: @- C) Z6 q) b
这里我们需要关注这个处理该SD卡请求队列的算法是何时申请的,也就是何时会去申请请求,何时会去唤醒内核线程。
* B* w; D! S/ c5 [. b. P/ n  `* c5 I5 P& F, N
用到回调函数q->request_fn有三处( b8 e- n# `& w: H. T
% C0 W, O2 @& V+ m/ r0 [
  • 块设备驱动注册请求队列blk_register_queue()
  • 驱动程序出错,清空请求队列mmc_cleanup_queue()
  • 实现请求队列机制的blk_fetch_request内部本身. W! ^* {% Z$ W" U- L4 e

4 q2 f" h9 Q3 T* A" W7 K* E
9 p8 S8 @8 h, v. w& w$ R( c# I% ublk_fetch_request()->blk_peek_request()->__elv_next_request()->blk_do_ordered()->...->q->request_fn
" Y3 u2 D2 `" m* o5 y
8 S/ d8 ^! T8 a( Z* H我们不必深究所谓的电梯算法,只要知道,它是使数据得以高效通信的一种算法,算法自身决定何时去唤醒守护进程处理请求。, w$ J3 J6 S8 O2 U& E

7 u" F: a& T' l; J1 p! A1 K2) blk_init_queue()7 {& I" e, `; f
' T- r+ J: e6 z9 S' b8 o
如果一个块设备希望使用一个标准的请求处理步骤,那就必须使用blk_init_queue()。这个函数注册了q->request_fn(这里就是mmc_request),并初始化请求队列的数据结构struct request_queue。
- p# K2 K/ p3 D+ t1 {
  f( Z2 t/ \9 H/*
3 ^) p0 m9 p1 r8 R/ @5 C0 f& G& D( z7 n) ^1 S0 V
*    call blk_init_queue().  The function @rfn will be called when there: p/ F4 J9 i0 N* o7 h9 A
*    are requests on the queue that need to be processed.  If the device
3 O6 }" K. w$ y, K) Y2 Z *    supports plugging, then @rfn may not be called immediately when requests5 r8 l: C8 t* G% `& @0 W/ s$ y, L
*    are available on the queue, but may be called at some time later instead.
- I; m8 b6 P+ ^0 G: d */
) ^; q; e3 n+ ]$ |2 ?8 A& [2 X  `& U# f! c. c
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)8 v6 i/ o! [* c
{- h$ M3 @! M7 ~# T- ^1 Z2 I
return blk_init_queue_node(rfn, lock, -1);+ _3 T% H: k- J/ `6 e, z
}
$ q! h+ l8 H) x) I' [6 D
* i8 {& K, W+ z0 {  }其中的rfn就是请求队列的一个算法,即这里的mmc_request。
8 t: Z, K  P9 b1 s
! \4 l1 @, g8 E4 c, b' n1 Rstruct request_queue *blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
0 E- z$ s( @1 P: J7 D% i* v: n{% l% T$ H4 m, A6 x0 r
struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);" N: n5 X$ L9 i. R9 x% {' |2 o
2 C8 ]  \: `# ?! z" M9 p+ h6 S
q->request_fn  = rfn;' P: J+ n: i9 C. Z* h  M5 z
q->prep_rq_fn  = NULL;
3 y" O' t9 m& X q->unplug_fn  = generic_unplug_device;
8 `! F) [2 {. S; N) R5 {1 @+ ]4 r q->queue_flags  = QUEUE_FLAG_DEFAULT;
, O0 Y3 a7 h, F2 M, } q->queue_lock  = lock;% i* D8 K& P3 W4 `
$ }8 b' ~4 M5 H$ q7 }2 h! p( N
blk_queue_make_request(q, __make_request);% c2 ]; j- d+ `5 c6 P; [
" i0 B( U! C9 E4 Q5 [# m
if (!elevator_init(q, NULL)) {: D1 L$ N: t- n; g+ @" n
  blk_queue_congestion_threshold(q);
5 a3 L# m* l8 A2 O1 A  return q;
9 ]0 y& @: ?" X! [+ z" }! b& a }/ h' U5 ]1 t# e! W( w( K, P: W
7 U# u7 l. B" P9 w! n
  ... ...' K% ?- u- y, q8 `* C% i

- C/ n: x3 u5 n1 c5 w& A}( W$ d8 J$ E5 t0 M) f( O

  F( v* q! [% W' [0 n3) kthead_run()( i+ }$ t8 N& {" B- e# B3 f
- U" G+ K" `" p. i. W6 ?0 R
注意到mmc_init_queue这个函数的最后,创建并运行一个名为mmcqd的线程,顾名 思意,mmc queue deamon它是一个SD卡的处理请求队列的守护进程,或者说内核线程,当系统注册SD卡块设备驱动时,就通过mmc_init_queue()开启了这 个内核线程。
- C" X( H/ K% q) O) |, l, \1 ^/ S+ L9 `; [
4) mmc_queue_thread 0 O$ f3 T3 `5 ], v# T) g
/ X, C+ t1 S8 @/ h7 L) G7 D' O
看看这个内核线程做了些什么,: [  M3 {7 ], J4 d9 J
, Y8 k7 q% {% W) T$ ], F
static int mmc_queue_thread(void *d)
4 b  r, D1 r5 z2 P* f7 Q{
0 [3 l) @* y7 A4 q! _ struct mmc_queue *mq = d;9 t: g; X- a; X* _4 ^( t
struct request_queue *q = mq->queue;
5 ?: |" ?- N# x3 m# C  R6 m) W0 D: O4 S" X  ]/ ~
current->flags |= PF_MEMALLOC;" @1 S; _; }8 ]/ C# q4 l5 S

2 m6 y6 Q1 `; H" g4 D6 x8 [! k down(&mq->thread_sem);; D/ I" d9 X5 c& B' t& m* Z

. m7 G( i7 N3 N; D, M% M; Y, u
6 r8 ^- X6 B* Z0 ^5 V1 r do {
" }, `8 G: i; h, K$ g9 a- ?2 A$ g  struct request *req = NULL;
! O8 @2 R* A5 U1 \! c9 P9 m: q
) z2 N$ s3 w& K2 j" D  spin_lock_irq(q->queue_lock);
" k' Y/ L) S2 Q( E  set_current_state(TASK_INTERRUPTIBLE);- {9 ^/ i1 u( l1 \
  if (!blk_queue_plugged(q))
0 d6 P+ k6 O6 a   req = blk_fetch_request(q);6 `, Y3 m9 ?3 S2 O
  mq->req = req;
8 P. [1 ~! a  d4 E! n9 Y8 K  spin_unlock_irq(q->queue_lock);
# z2 R. d" F2 s3 j; r# r# [" Y$ g& s* S' F1 I& L% \" |
  if (!req) {
+ N2 |$ M2 e$ |# W5 K% d% `' y3 H6 K8 _" r+ T! G/ Q
   if (kthread_should_stop()) {- C2 [& C7 \% `2 Q
    set_current_state(TASK_RUNNING);
; b! v/ y! i3 ]  w! M    break;8 B0 {8 Q- e  r; g1 t$ z) p! S( S
   }
5 ]% d( y/ Y$ V: ]   up(&mq->thread_sem);
) M: {! Q6 Q/ G  J; m8 L, A0 @  g/ r   schedule();
) s# t/ L3 j, n9 F4 d: `$ H   down(&mq->thread_sem);7 |+ L' @8 C/ D' c
   continue;
- A& C! b% _( v( F: Y+ G7 q  }) `$ y5 v" Z2 H& x. t. F3 @+ w! i( G6 J
  set_current_state(TASK_RUNNING);
. U6 E7 b* {* a' M' x' N. w+ o
; B, o/ p* \, C, A; y5 E/ W  mq->issue_fn(mq, req);
- k! x" k/ J9 I: L  h } while (1);. J& e5 j% i2 j9 O$ B
* c( i2 x  C6 Q8 A0 W% [
! a! v8 f$ S9 H# W& H
up(&mq->thread_sem);
, e4 N, x: Y& Q* y! b8 f* B& \# T4 G8 B/ R2 @
return 0;
; S9 T* x9 y9 w/ k4 t}
$ s1 W9 }: |" f: _
, C; R* G% q0 P( z  s首先,这个守护进程是一个while(1)死循环,如果没有特殊要求,即kthread_should_stop()指定要把这个内核线程终止掉,那么它将从系统启动开始一直负责处理SD卡的请求队列。* T# L: U- V0 G' r" Z& L& |

5 A: H/ F1 }1 r; _2 S- o在循环内部,内核线程首先通过 set_current_state(TASK_INTERRUPTIBLE);设置当前线程为可打断的等待线程,进入睡眠状态,等待其他线程唤醒它,这 里唤醒它的就是处理SD卡请求的mmc_request,当mq->req为空,即当前没有请求正在处理,则通过 wake_up_process(mq->thread);唤醒内核线程,接着该线程尝试从请求队列里得到一个请求req,& A4 P" @; [# @/ Q1 B( [  n# L
& x2 x+ G7 M. K& d! D0 ~4 k
-> 如果没有请求,则调用schedule()交出cpu的使用权让其自由调度,等到系统空闲时,再次得到cpu控制权,并且执行continue;退出当前循环,重新开始新的循环。
9 g% Y* Q. v' m+ v
) ~' k" k1 t7 y-> 如果得到了一个请求,则通过set_current_state(TASK_RUNNING);将该内核线程设置为当前正在运行的进程,并调用issue_fn(),即mmc_blk_issue_rq,处理这个请求,实现主控制器与SD卡的数据传输。
; i9 C4 c1 _9 w/ {1 @
2 [0 O: p* n- x1 u8 w3 E/ C5) issue_fn
# _% s8 {' n2 G) w+ A" k  w$ p1 C! k6 N, _" r7 t$ F
驱动初始化函数probe()里的mmc_blk_alloc()里注册了这个回调函数,md->queue.issue_fn =mmc_blk_issue_rq;
- F: h/ G1 @% C! g% j4 U9 o; \; B  Z$ T1 z9 m2 O
这个函数将req里的成员解析成为mmc_blk_request里的指令和数据,即mmc_command和mmc_data,然后通过mmc_wait_for_req()最终实现主控制器与SD卡间的通信。- G6 L0 u$ r2 \

. j3 \: L6 r7 b3 M! {0 Wstruct mmc_blk_request {
, v# q# _! {% z4 n5 P" f struct mmc_request mrq;
5 {5 ^( `) W" v8 N( N struct mmc_command cmd;
* ], K) u  a9 @3 ?; C  W struct mmc_command stop;
' Y) v% I' ?* T" s: L struct mmc_data  data;
( J- @" b* U* H4 H) \};9 r/ ^. Q9 G- y& L2 `# H+ _! A/ p0 B
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)4 Y0 M5 G' e7 l) x  _
{
" @4 e% `; N' D5 @5 N struct mmc_blk_data *md = mq->data;
, A7 k  `! i0 t) J8 o struct mmc_card *card = md->queue.card;
" k' B3 w! Q8 O2 N4 X. @ struct mmc_blk_request brq;) n& i, z/ g% ~
3 K3 c2 i  [! Y
mmc_claim_host(card->host);
: o5 c9 H( P3 c7 D5 c0 L
9 E" b6 I- ]7 b& @* }4 L: g do {3 K8 r( H/ v- w, w2 G% T

- v0 |/ [5 P4 A% Z  X  brq.mrq.cmd = &brq.cmd;
' {* Y# [& f$ ^  brq.mrq.data = &brq.data;
% Z3 R( l$ p$ s1 i3 k+ b
/ j8 t0 j3 q( e  O  brq.cmd.arg = blk_rq_pos(req);            // 解析指令的参数
6 A$ S. W% K; _- ]  ~5 j  if (!mmc_card_blockaddr(card))
( l; E* S6 ]' H% S  a   brq.cmd.arg <<= 9;
& I6 M' g) M& R3 e) S  brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;- P) {, z% Y+ X# r
  brq.data.blksz = 512;
, K8 P2 H; V6 S- @0 [# C4 v% ?  brq.stop.opcode = MMC_STOP_TRANSMISSION;" R, j$ \- u3 e* K
  brq.stop.arg = 0;3 ]# T% n" l& ~- e( P$ q- K
  brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;4 t! Q* J" c3 U# x" C. I
  brq.data.blocks = blk_rq_sectors(req);  // 解析数据块大小; n5 n( N" x9 I
4 E( P( a1 N4 j! }* k" m! c
  if (rq_data_dir(req) == READ) {              // 解析传输数据方向
- c( O& ?' I% u. k9 R   brq.cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
3 ?6 o6 v# o# |3 ~/ g   brq.data.flags |= MMC_DATA_READ;7 o# \0 L: r, m9 J- U4 b
  } else {/ I: ?4 B- i( c/ d
   brq.cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
5 [& F+ k5 p- G- w4 T   brq.data.flags |= MMC_DATA_WRITE;
% O8 z) w3 n; H. R  }
) A" W7 m# c8 Q9 T/ y% L
# i' @3 P- R5 J( q8 I) Y   mmc_wait_for_req(card->host, &brq.mrq);  // 调用core协议层提供的接口函数,实现数据间通信) M. c( Z3 C) y4 l

( H9 @  s. U# X* [4 J+ V   ... ...0 t( s% e; m  m' Q6 C
1 Z4 q, M6 {% a
  }- S# Q1 `) K5 a
+ ^# m1 [, q  t
  mmc_release_host(card->host);; Y8 {* a2 Z8 _& ^+ d/ R

* m, H' \3 }8 P. \# `2 H) _}

+ y! D! ?0 V  a$ c
5 }2 \- U8 k* R
" e) x, x: u) Z  y: a, y

5 S- @/ J8 n$ k4 [
, p) F4 a+ G  G4 Y1 O7 T$ `
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-25 17:49 , Processed in 0.312500 second(s), 24 queries , Gzip On.

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

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

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