|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的。在分析过程中,本文将以platform总线和spi主控制器的platform驱动为例来进行讲解。其实,platform机制是基于driver-model的,通过本文,也会对platform机制有个简单的了解。
" y$ r8 V; L4 T- V
5 K; E) g- T7 m( l5 R( b1 V- \$ a内核版本:2.6.30
# i% k$ N+ f* I* p5 D
1 u; F- Z/ f8 r- e1. What is sysfs?
9 ^0 L6 A, M5 P* ?; z R2 E 个人理解:sysfs向用户空间展示了驱动设备的层次结构。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。
/ }* A' d. N' W8 ?" u. A+ y' f" h' J) ]6 K
我们来看看sysfs的文件结构:
5 j0 L7 r4 K. i+ u$ U' X9 L9 m: g
/ u7 m4 |0 I8 S[root@yj423 /sys]#ls, e. V# ^% u$ Q" m
block class devices fs module
: M6 B$ l3 J/ u' w$ mbus dev firmware kernel power
2 e) n4 D8 Y/ v% F9 P; B
0 U Z' X2 F5 `% dblock:块设备5 F( D+ `4 D3 s
, Z; d$ | v/ ^: W: Abus:系统中的总线0 e! _1 J/ R, F; o5 g7 S9 S' q% c
: D ]# F: S& _$ g7 B) J
class: 设备类型,比如输入设备
- q; i4 p4 l0 U( L& B, v5 A7 G$ {% J9 @) Y) r9 ^# A0 X0 O
dev:系统中已注册的设备节点的视图,有两个子目录char和block。" l% Y5 X3 F/ u# c$ F0 h3 f8 f
5 e1 a) W6 M5 i s" tdevices:系统中所有设备拓扑结构视图
/ l B+ E7 Q L. m0 O" P6 i4 ?( s, {3 |# J; q; K0 z- o; U
fireware:固件
2 L2 l$ `- B1 l& T. V+ }
1 i! l( _+ _# W3 Q3 ifs:文件系统9 n+ U, m' @4 w; v& M* i2 w- ~
) x" R! |. G' [( z
kernel:内核配置选项和状态信息
1 M6 ]$ G" @* E b9 X; c; _+ p% B$ q/ f1 p/ e5 W7 c1 \
module:模块( F: v" ]' K2 |9 W
; j( n1 H+ j& j e
power:系统的电源管理数据" B+ z7 v8 P4 S: X& H/ X
3 i Y! @! s# P S+ w
2. kobject ,kset和ktype0 S0 G# d6 K, X" B2 w; t( v& c# h
要分析sysfs,首先就要分析kobject和kset,因为驱动设备的层次结构的构成就是由这两个东东来完成的。# k6 `' s( c/ \. \: J
& J- z8 t( I7 }% W
2.1 kobject3 p& {8 H6 \+ J! n: w. @
kobject是一个对象的抽象,它用于管理对象。每个kobject对应着sysfs中的一个目录。4 r. A/ q% p7 f* t
$ v3 H, g4 G6 B
kobject用struct kobject来描述。+ M6 g2 q; m( Y/ \9 x+ x
* y% l+ G( A& J
struct kobject {7 c' `9 Y& t# F1 c
const char *name; /*在sysfs建立目录的名字*/
8 p: U# U% q( J: I struct list_head entry; /*用于连接到所属kset的链表中*/
) b P; A. J4 e( H struct kobject *parent; /*父对象*/
/ y: J1 M+ S( N/ a" Q struct kset *kset; /*属于哪个kset*/
) d- f& Q1 p' ^$ c/ X: j struct kobj_type *ktype; /*类型*/
0 k8 o) S6 ~1 n struct sysfs_dirent *sd; /*sysfs中与该对象对应的文件节点*/
Q( d$ _! `$ S8 |/ |2 _" L struct kref kref; /*对象的应用计数*/ x5 g- A6 O' l, R1 h
unsigned int state_initialized:1;, s) l7 L. x6 [, l
unsigned int state_in_sysfs:1;# B: ~" v' X' e, Q5 V8 I
unsigned int state_add_uevent_sent:1;' c) R( a, }' e
unsigned int state_remove_uevent_sent:1;
8 t/ V6 J3 f+ T2 p! K- W7 T9 } unsigned int uevent_suppress:1;
: T: |: m; }; e& x% g1 m};
7 h) z& Q% ~* b4 Q2 D2.2 kset
9 T9 J+ j/ X3 V# Z kset是一些kobject的集合,这些kobject可以有相同的ktype,也可以不同。同时,kset自己也包含一个kobject。在sysfs中,kset也是对应这一个目录,但是目录下面包含着其他的kojbect。
Z$ q0 P: A- k' u6 ^8 q2 D* e
+ m0 h$ N7 X, I+ d+ ^; c kset使用struct kset来描述。+ g5 ?( |2 n1 w' C6 F
: Q2 o: s8 }/ k/ l$ e/**
& T! B' v ~ H/ y' M * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
) p8 c7 u2 ^( v n, L *
* f# n1 V' n9 ~ w9 i, u6 F * A kset defines a group of kobjects. They can be individually( h3 \4 m- F9 ?$ _1 I2 Q
* different "types" but overall these kobjects all want to be grouped
- k q5 V j9 J2 q * together and operated on in the same manner. ksets are used to
1 q: R. i7 {# B2 a0 w7 E' f) t$ V9 U. f * define the attribute callbacks and other common events that happen to
\0 k) o6 v5 M * a kobject.% B k; L4 B, \( O: P+ J' I* b
*
5 i) w6 m1 t: q6 X' k * @list: the list of all kobjects for this kset' k" c& K1 F$ l5 N9 U/ r
* @list_lock: a lock for iterating over the kobjects# a8 U G4 \+ K; N1 D+ J1 I
* @kobj: the embedded kobject for this kset (recursion, isn't it fun...)0 Y h* w) ~/ H0 Z4 z1 P" W8 u
* @uevent_ops: the set of uevent operations for this kset. These are
x% ~; }2 v! W- D3 Z. d [ * called whenever a kobject has something happen to it so that the kset0 i0 u' R P/ [& d" I: Y
* can add new environment variables, or filter out the uevents if so
4 h1 F( B+ n: z2 U; @9 ?# M * desired.
, Y# B } p7 I$ z' y/ `0 W */
# b- l- t8 R7 t% S+ Z1 _struct kset {
1 \! Z- `. y8 G1 s struct list_head list; /*属于该kset的kobject链表*/; ^2 U2 G! S" y
spinlock_t list_lock;
* w2 o9 B U. K' I# I struct kobject kobj; /*该kset内嵌的kobj*/
: N% D8 I+ d3 M
& j1 G6 k0 i* ~( ?" C struct kset_uevent_ops *uevent_ops;
7 [5 s/ [+ U" o" t9 o: M};
0 A6 m8 I! E# i; `6 j! V \% Y- f9 }+ u t5 N
2.3 ktype
; f. S) p3 J# d! I: E6 U6 T每个kobject对象都内嵌有一个ktype,该结构定义了kobject在创建和删除时所采取的行为。
2 C: A+ M& ^% V/ s3 b% o7 j! S7 e: q- R9 H
struct kobj_type {* B! i8 `% M% b& y
void (*release)(struct kobject *kobj);# q7 M3 ]" f c5 S
struct sysfs_ops *sysfs_ops;, ?+ R. M; ]' P f) v
struct attribute **default_attrs;
5 ]6 X( [, X* J/ n( B- r};
2 h+ R( _/ V; s4 v4 [
i" u; I7 q7 A2 pstruct sysfs_ops {
/ _! M3 p+ f- b9 L. E: G ssize_t (*show)(struct kobject *, struct attribute *,char *);9 L% }" Q g0 ^* c
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
$ v n1 m9 [/ d4 O( L2 T};, q' z1 D+ D) F
: @& g8 e! L6 d5 B1 y/* FIXME
' [3 D' A5 I" E' Q! m * The *owner field is no longer used.
* ^, {# l# m- @2 B * x86 tree has been cleaned up. The owner
3 U+ L8 t- N, P5 V1 Y" G/ O * attribute is still left for other arches.
7 Z5 o( U' [" h6 n1 ]& R9 W0 P */
9 |1 x: C! w; ?; n, dstruct attribute {6 q0 i6 e; Q) O* K" z( N
const char *name;/ x1 F( F* V, f' S- e
struct module *owner;. X6 {! ~9 _, I4 I
mode_t mode;, K1 y5 Z% s+ K" G6 e) I% \* D4 o; X
};; f& s# o. ?/ E3 N+ [2 w' ~8 y
6 G; z1 t0 f# g! R: W: V
B" ^& p7 Y/ K# w
当kobject的引用计数为0时,通过release方法来释放相关的资源。
+ f5 i1 K7 x+ G1 z- Fattribute为属性,每个属性在sysfs中都有对应的属性文件。
5 m0 D4 t' V0 D9 j. s- E& \; S0 _: h! l5 E7 A6 T4 N; H$ q
sysfs_op的两个方法用于实现读取和写入属性文件时应该采取的行为。
6 Q; F/ m: B# i7 z+ L6 l# A* d: b6 d2 v0 x* D; p
2.4 kobject与kset的关系) e; [ ]# M m* G ^/ `- ~" B
下面这张图非常经典。最下面的kobj都属于一个kset,同时这些kobj的父对象就是kset内嵌的kobj。通过链表,kset可以获取所有属于它的kobj。4 Y8 b& A0 ?: K$ L! D, b0 {0 U
$ Z0 d3 l& g! \ U
从sysfs角度而言,kset代表一个文件夹,而下面的kobj就是这个文件夹里面的内容,而内容有可能是文件也有可能是文件夹。
8 m8 b3 F0 C8 Y& R. ^- ^9 h( n' [# j; _/ n5 C2 y! P* e
) r# {: _7 [3 D, h9 z
8 T, b" p* s- f0 U9 U1 z9 a3 N- Y3.举例
% H& Z" l/ L, C+ P# ]+ n! l( j$ B在上一节中,我们知道sys下有一个bus目录,这一将分析如何通过kobject创建bus目录。
+ {: k7 s+ s: W5 \8 X
1 H) R" }+ D1 L' g+ _下面代码位于drivers/base/bus.c; @6 X3 a0 [5 v1 e9 N1 r
int __init buses_init(void)
" X$ f7 W4 j* ]. T, H4 U& M{8 ]" l, ^& W1 `* \. Z
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
7 `& I5 t/ [7 Y3 R if (!bus_kset)+ E& u/ d# x, S% l* L9 I2 f
return -ENOMEM;
, b* n$ @4 j' h, n' V( w6 D7 M return 0;
+ Y2 h/ {, h( y( T$ J) j+ g5 d; a}
/ l; O, o! y& L9 H) d) v1 }
! j* Y( j1 W, }$ N5 B3 E/ {static struct kset_uevent_ops bus_uevent_ops = {2 D1 t# g" F5 \
.filter = bus_uevent_filter,1 D4 ^' C. q, a2 U
};
8 n' n& j9 z. N/ S$ c1 E$ |4 S$ _* |( f3 l' {- e5 u& B
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)1 b/ k& w# D4 W- Q! X
{+ D2 h8 V' q& C
struct kobj_type *ktype = get_ktype(kobj);
" B6 _" f* i/ ?7 v# [9 B6 P! V" @( U% ~" o% Q2 e# f
if (ktype == &bus_ktype)/ C: w7 m! Y# i h. g9 C9 C
return 1;8 |7 t1 ~* Y! s T6 ?& Q& b( ^/ R
return 0;% s0 C8 X' R5 b1 V- Z
}
3 x* A# P7 }2 H8 |% z9 \) h这里直接调用kset_create_and_add,第一个参数为要创建的目录的名字,而第三个参数表示没有父对象。
6 z* Q9 ~6 O" S下面代码位于drivers/base/kobject.c. |, f$ v8 ?9 Y6 R4 z9 D
/**
1 B. |/ \% R6 r' O * kset_create_and_add - create a struct kset dynamically and add it to sysfs1 H7 b R, D7 ?8 j: c9 j
*
V9 z% \: e3 Y2 I1 S* D' q * @name: the name for the kset. f d- c2 c3 ]2 e, }
* @uevent_ops: a struct kset_uevent_ops for the kset; e$ i( T- I# X3 c8 \9 d
* @parent_kobj: the parent kobject of this kset, if any.
; `0 b+ g( _8 Y6 ~4 x *
- [8 {1 @2 n3 m# Q * This function creates a kset structure dynamically and registers it
* p1 \+ v n/ R * with sysfs. When you are finished with this structure, call
, |9 l$ Q- o8 j) f * kset_unregister() and the structure will be dynamically freed when it8 e) T5 V4 K) x2 ?
* is no longer being used.6 w6 x5 l- v& K6 \2 Z% g0 h( |
*
' q' t/ l( g$ x p4 ~ * If the kset was not able to be created, NULL will be returned.9 N% D/ |; d4 Z& B/ n
*/, Q: N# H$ z2 T, b& `5 ]
struct kset *kset_create_and_add(const char *name,- K. V% {0 ^) {( A# z& N- f) d4 r
struct kset_uevent_ops *uevent_ops,- W- }" C( |% }( D; Y
struct kobject *parent_kobj)
3 t' K# i* V6 K: ?( C3 G% D9 C- T{
2 h- n R/ P# G struct kset *kset;# j5 y: a2 o! R! M/ n! h( K2 b8 j& i
int error;
" J/ `0 w/ S: A( O2 W
$ o$ l" O: G ?5 t, k kset = kset_create(name, uevent_ops, parent_kobj); /*建立kset,设置某些字段*/7 P/ \ }( O7 O
if (!kset)$ A7 v4 Z, w7 C6 s {( C3 V( O
return NULL;3 _% w- e( y0 X
error = kset_register(kset); /*添加kset到sysfs*/
" I) n6 P ~/ }# m2 e if (error) {& N9 P4 z; w; t' c/ G
kfree(kset);
' I H0 q4 X" s3 m& G) u% A return NULL;
- S- `" b0 F! z: `& \8 ]5 s7 s& g }
0 d2 y; }+ s' Y$ d$ k h* T0 R+ t! _ return kset;
% s: e* V4 b# T}
7 \) A5 ^& s" ?" B" c, f这里主要调用了两个函数,接下分别来看下。9 l" G4 e0 O- X4 D- b9 ?7 q; H
2 _ g- x* i- L- G r1 u
3.1 kset_create函数. R" U( R" s1 U& X# T8 i
下面代码位于drivers/base/kobject.c7 ~! I8 n+ q6 W& y, r0 c
0 W7 y% Y, | S- [0 O2 z/**
) ~ V! K& v9 w$ g* C * kset_create - create a struct kset dynamically
0 i4 z' w" B0 e9 r4 j- v8 _ E. K9 P *6 r: e0 ^' N R3 U
* @name: the name for the kset
9 a" R z3 S u7 _ * @uevent_ops: a struct kset_uevent_ops for the kset* A; J/ A7 Q+ G" `' s
* @parent_kobj: the parent kobject of this kset, if any.
( E. n, S$ _! ] L! q8 } *; L* Z; W, G+ x* n) m ]
* This function creates a kset structure dynamically. This structure can5 C% T' _7 t2 r! F% W5 B3 B
* then be registered with the system and show up in sysfs with a call to1 t2 X& G" b$ P9 U+ N6 T: |
* kset_register(). When you are finished with this structure, if3 B( ?" C5 s, `! Z k
* kset_register() has been called, call kset_unregister() and the7 t" i& U! H3 p/ }+ y8 U
* structure will be dynamically freed when it is no longer being used.
* A! |* K/ n+ v *
2 P/ {. Q* k( x8 t; y * If the kset was not able to be created, NULL will be returned.
/ ]( }/ B( m; D# u$ } */+ b$ Q" B( ^& V4 [& Q# b
static struct kset *kset_create(const char *name,# ^ M5 G% H# g g( ]& g
struct kset_uevent_ops *uevent_ops,
6 n# n1 y4 A0 f, ]) V struct kobject *parent_kobj)2 H* p: T9 c- K# P
{2 ]; {8 d: F6 U t( F( B
struct kset *kset;. [9 l, \4 K' y) `, i, r) e
; Q8 k# L: H, E4 V$ m% E
kset = kzalloc(sizeof(*kset), GFP_KERNEL);/*分配kset*/
. ?/ V Y; z7 P# T& t- B% K8 c. ? if (!kset)
+ o! |, f2 g4 P! B- i$ s& q, |7 J return NULL;5 @+ I) L% t0 u: \! i2 y
kobject_set_name(&kset->kobj, name);/*设置kobj->name*/: { [8 F* p }: w) ?
kset->uevent_ops = uevent_ops;! M+ Y/ A) j! F
kset->kobj.parent = parent_kobj; /*设置父对象*/* K1 Z1 i& j( u
- c6 T& k1 J% k6 s: Z; x
/*: L3 p, `& s) L6 R
* The kobject of this kset will have a type of kset_ktype and belong to8 M" p, {: n4 Y
* no kset itself. That way we can properly free it when it is
: L% I: X7 j) j% C" `; }9 w * finished being used. F" G2 @9 g# S7 Q2 C
*/, i: G& u9 m+ J3 S' Q2 B! B Z; X
kset->kobj.ktype = &kset_ktype;+ W l& Q6 ^9 d; k0 t
kset->kobj.kset = NULL; /*本keset不属于任何kset*/9 I0 F }1 o. @7 k- C; f
5 ~: u% b( |% [6 }2 L return kset;, z j/ p z4 \ `, p% b9 b
}# P0 a9 T& F. u! A% P
' Z1 q, E- j7 h& A, C这个函数中,动态分配了kset结构,调用kobject_set_name设置kset->kobj->name为bus,也就是我们要创建的目录bus。同时这里kset->kobj.parent为NULL,
8 p7 }/ x2 Z8 R. Q n* q4 C T2 B
也就是没有父对象。因为要创建的bus目录是在sysfs所在的根目录创建的,自然没有父对象。
# }2 l. w( M% F$ b' ?2 P' X% ]* E4 M7 L1 H8 t, t
随后简要看下由kobject_set_name函数调用引发的一系列调用。
8 m2 E# @" b9 l3 R6 X0 L/ |& o
+ n) \7 @6 N; j; r2 M K% a/** _& u) _4 l1 H" q
* kobject_set_name - Set the name of a kobject' ~7 M1 g6 z: M* K# v9 }7 N
* @kobj: struct kobject to set the name of
" [0 ?- ?+ @$ x4 M' x0 e * @fmt: format string used to build the name
1 v, x5 {' c& Q: c8 i6 C2 L u' O *& d: j, O9 u0 k5 x) Z1 K. c/ J
* This sets the name of the kobject. If you have already added the8 A6 o) E! D$ m; [" y
* kobject to the system, you must call kobject_rename() in order to
3 q! W) d; s) k2 W7 H; `2 X * change the name of the kobject.
t" ^- q2 q0 ]. F' B- k( }! \6 { */1 c$ J+ |6 v. {3 {. z6 s
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
( \$ G, p$ Y8 [0 X{! E7 A9 L- ]5 C8 h9 T7 u* R
va_list vargs;
( \/ ]* ^% D; U5 m int retval; }8 V6 g: t$ j5 c6 e2 @* C! G( ?8 E. I. a
; F* K* W% s/ n- |% ` va_start(vargs, fmt);! b0 w( |) s5 H/ n$ x6 X
retval = kobject_set_name_vargs(kobj, fmt, vargs);& Q" [5 |5 a: N1 I
va_end(vargs);# J3 u7 G, {8 C g
\7 Y0 l; O0 @% k" j
return retval;3 l8 D+ |+ F7 b3 X, Q" g* T5 j
}
1 S" Q0 n3 Q- h* E
% _( J ~/ F! b/**
- E; J' _* \' E( x7 z z5 k * kobject_set_name_vargs - Set the name of an kobject
; v$ s6 p+ o( b# h * @kobj: struct kobject to set the name of
6 f% g- K9 y( H' \ * @fmt: format string used to build the name
9 S5 y& e8 f0 G1 k * @vargs: vargs to format the string.+ D, A# e( C3 b* a+ O7 L4 Z
*/
" O4 n# E# A2 H8 h5 w* [3 `+ ^int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
: b- U2 R: {4 B: e" j va_list vargs)
6 `) ]5 H. c) z& r- A2 y& ^$ F. s& W{
' m6 C4 r0 B; ?) c e const char *old_name = kobj->name;2 ?9 \6 C1 p' _' m+ C! Y2 W f
char *s;
3 k5 B3 U1 l+ W- ]' S& H; ]
' t1 G3 |; u# B' v3 m9 x6 }8 C7 I if (kobj->name && !fmt) O( \- F4 d- E1 p
return 0;
0 }6 c+ h! A& i9 L, ?( f
" g7 G" p9 P' q0 i9 i5 [; j) `* H2 R kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
) y. P; K: o7 ^ b" p+ J if (!kobj->name)
6 F+ R/ M9 p2 ]8 {- n" a return -ENOMEM;
* u( j8 H' Z. }; A* g6 {$ j% T( l: F) E+ a4 S' N
/* ewww... some of these buggers have '/' in the name ... */
: z) n9 S& \; t/ ` while ((s = strchr(kobj->name, '/')))) G" K: N2 `; @3 T, _
s[0] = '!';1 l3 y. j4 i3 {+ _( a: G9 b7 t
( i0 q. v! ~# {5 E
kfree(old_name);
; e! A& ?5 Q7 ^ return 0;
6 Z6 Q* w( ]6 x, p4 M; d}
0 H9 c C% W j/ N. N2 ?, k- C
* F' k0 s( @$ `1 V7 s9 t/* Simplified asprintf. */
6 K- `5 I' w, l$ `0 m/ C! i( Cchar *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)! p. g) z6 M5 ]
{3 m) z2 @" S) ?* J# q
unsigned int len;
: s: F1 B+ |, {2 y- S char *p;- y* [9 d& ^# L& X& b- U4 `
va_list aq;
) r1 A% f( }( a3 o* J4 v7 i D) J1 M2 \! n# X
va_copy(aq, ap);3 v$ d# y" W% G# W) o6 _) X3 j
len = vsnprintf(NULL, 0, fmt, aq);
! v* z$ G( P: L# ] va_end(aq);, g# R5 ]! L# O
# h% F% L; X8 I2 Y! M" v p = kmalloc(len+1, gfp);
$ a4 u* \7 v" s: y9 A) p if (!p)& f/ G0 f, d e! b0 b( y+ N
return NULL;
) v, G9 m% o) D5 T
; {' k9 F) ~5 V4 E; n+ T% g% Y& y vsnprintf(p, len+1, fmt, ap);. V. X/ g- _. H$ a
7 a9 e g6 ~0 u- y5 z
return p;
" C( V) J2 s; \/ m: e}% P% S! w" T, s. A: y
3.2 kset_register9 L6 v E& Z+ [" a2 b# U
下面代码位于drivers/base/kobject.c。
7 m- H0 \7 ~8 u/**; ^7 ~1 _7 F, y5 d5 s
* kset_register - initialize and add a kset.
# H, i! K3 _& _ m8 m * @k: kset.
; L0 `0 c( d. X0 T; j */% S$ b$ d5 P& g# a( C' }$ h
int kset_register(struct kset *k)8 E; e3 C4 d8 H9 t
{
* m& |" N2 |, t- c* X int err;+ Y; e) o. B% M. i' \
' L% L. l+ o; J7 j: Q
if (!k)+ `3 d1 Y- M; s, t- W) N: Z$ r$ R
return -EINVAL;6 |! I# u( v: M
+ c4 V" k- \7 P8 P. w0 s, ^ kset_init(k); /*初始化kset*/
! ~6 p/ F) O, D$ t5 h' E err = kobject_add_internal(&k->kobj); /*在sysfs中建立目录*/. [# H. i( |: E2 V( B
if (err)/ j3 N6 n2 g+ L4 C8 O! o" S
return err;5 c2 R3 e: U- h3 o$ u
kobject_uevent(&k->kobj, KOBJ_ADD);' K9 q, X) n3 S! l- }: G
return 0;- v4 B/ m8 z' M1 V8 g, ^. L
}
0 q' }+ o/ t; t3 c) ?0 j0 Q4 W/ A5 c这里面调用了3个函数。这里先介绍前两个函数。
. E9 ~2 B% j; Y7 e0 A% D, p1 ~3 n( [1 f" h6 f0 [6 S) f. n. ]; O
3.2.1 kset_init" g# S6 H- E4 d* z2 U+ j- B5 V9 [
该函数用于初始化kset。' u% P4 x7 E0 G+ S0 C( D4 R9 y2 s
; V' |5 }$ G+ H 下面代码位于drivers/base/kobject.c。
7 v# O0 b8 C& q+ `! r* V+ \/ F( G. T& I1 k. ~1 i) }
/**2 R: ?1 s9 j( t; G: K
* kset_init - initialize a kset for use! m8 I2 G& N( L6 b5 y
* @k: kset
8 } r. Y5 D8 x# M7 m5 G9 a* r* P */
L; x* C% l4 E: m. Avoid kset_init(struct kset *k)
/ v+ \- ~/ X7 Q0 M{
; _+ M" D9 ^8 G3 l7 g1 |( R, w kobject_init_internal(&k->kobj);/*初始化kobject的某些字段*/
7 ^* J0 j/ Z# y% Q# m INIT_LIST_HEAD(&k->list); /*初始化链表头*/
2 k) G" d$ n' }+ p: }' c( E# I% S spin_lock_init(&k->list_lock); /*初始化自旋锁*/
' I4 K$ v3 g, i}
- b! \8 C9 u \! Q# _
- G$ F2 D: A1 x0 Fstatic void kobject_init_internal(struct kobject *kobj)) \. m# ?0 e) K7 d' h
{
( P. y! s( U& Q: p6 b$ F$ G3 q if (!kobj). i4 d" j- M9 s! c# T! U% \' E
return;4 i' H& O5 {# J% I6 r' q+ [/ ~
kref_init(&kobj->kref); /*初始化引用基计数*/
, d5 Q% J& R& M0 u. s INIT_LIST_HEAD(&kobj->entry); /*初始化链表头*/
3 e; t, R+ H) e$ J% t; T% a1 ^ ^ kobj->state_in_sysfs = 0;' v+ p: Y0 ~/ O. s
kobj->state_add_uevent_sent = 0;2 b4 _8 S0 B3 ?
kobj->state_remove_uevent_sent = 0;
* O4 M% F' U: [. W4 d2 O kobj->state_initialized = 1;3 v" \; z& y# d y: h) f: d
}% e0 N& y1 i) O, a) b% k
3.2.2 kobject_add_internal: [# u; o" [1 r; I% O
该函数将在sysfs中建立目录。, ~5 R8 Z# n6 }/ j0 d( P
- p Y6 [2 Y8 |- ?! b9 @" h
下面代码位于drivers/base/kobject.c。
+ ?' c/ [3 Z: U6 _3 Z estatic int kobject_add_internal(struct kobject *kobj)2 G. g. c3 L) Z$ h; w1 V
{
, K5 G0 V+ h9 A0 t$ [' J, K int error = 0;( }6 R6 ]: c5 s# Y0 J
struct kobject *parent;
. x, p( {$ ~7 d! i4 L3 y$ y2 | s8 N: l( b" A8 }8 ?6 |- V
if (!kobj)& \4 _1 n' H* e+ m5 k& f! G
return -ENOENT;5 h6 J; r" F' G& h8 p3 {5 I
/*检查name字段是否存在*/1 D8 h# _+ q' X. i! _5 B6 E" R
if (!kobj->name || !kobj->name[0]) {
6 e2 V* e5 X9 s WARN(1, "kobject: (%p): attempted to be registered with empty "$ Z& ?& v5 ^, T8 |* h
"name!\n", kobj);
: a) g8 q3 Q5 P5 X* X/ p return -EINVAL;
2 g- l/ M, J! w }1 \2 i9 b; P z+ E
: `- a% f7 ?# m7 p
parent = kobject_get(kobj->parent); /*有父对象则增加父对象引用计数*/3 V& j# W# {9 b* q
+ r) [) ~' D8 P: O; O/ j
/* join kset if set, use it as parent if we do not already have one */) |4 N7 m, [; w" X1 z1 |; s6 F
if (kobj->kset) {
. m! k: \2 p# O/ w' D if (!parent)
' I$ _+ Y2 X9 s, T* n" E /*kobj属于某个kset,但是该kobj没有父对象,则以kset的kobj作为父对象*/
" [9 R' Z1 \* e! W2 N parent = kobject_get(&kobj->kset->kobj);
3 T' R( K. P7 N; a kobj_kset_join(kobj); /*将kojbect添加到kset结构中的链表当中*/
?: `4 ^% M1 f kobj->parent = parent;2 Y( v* i* \" ^9 O# I
}
' n$ |6 ]& e, j) ~* {1 P- O* G# e M
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
+ A2 v! f: ?/ F8 p9 F. T kobject_name(kobj), kobj, __func__,
& b1 ?+ G1 w+ V$ Q" ~: v parent ? kobject_name(parent) : "<NULL>",# d8 } c* c A: M# w3 T
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
. ` P3 a5 O2 G) i$ S0 f% `
2 ^- i) z2 I! R error = create_dir(kobj); /*根据kobj->name在sys中建立目录*/
$ Z9 l" U& y1 [9 {& j$ G3 H if (error) {
! P* D! m: F4 a2 X kobj_kset_leave(kobj); /*删除链表项*/4 V: l0 a( b4 W: |( e# y" R3 G
kobject_put(parent); /*减少引用计数*/
' z% [4 J9 ?# C1 h$ Y kobj->parent = NULL;8 l+ D2 B- ?: v, }9 X9 n# A" J6 B
v5 l9 Z- K9 r5 U5 H* v9 n /* be noisy on error issues */3 y1 m) a8 i; B/ a
if (error == -EEXIST)" Q! e. q& f( g ?* u: m
printk(KERN_ERR "%s failed for %s with "
9 [2 L9 E2 `. H' O "-EEXIST, don't try to register things with "' z% `% l4 h4 K1 C- r
"the same name in the same directory.\n",
% b2 d# g) n. Q) X- u __func__, kobject_name(kobj));
' h0 _" @( G4 i+ ~# j8 } else
/ Z8 v. D# g3 B9 ` printk(KERN_ERR "%s failed for %s (%d)\n",
# t# V3 B- t4 @& F) N+ J __func__, kobject_name(kobj), error);
; Q( t/ z, n. @7 ?; W dump_stack();* t& _) k @- `2 @3 G4 T* f
} else
7 o8 P' V' L$ }( L$ ] kobj->state_in_sysfs = 1;, o- u3 J# O; ~0 a& Z/ b! x
$ z6 k- A0 O7 D) b6 S
return error;8 v, i6 ?3 T5 C
}
) K- a8 p) h# G
# ~% d' q) C3 A3 V4 f在上面的kset_create中有kset->kobj.kset = NULL,因此if (kobj->kset)条件不满足。因此在这个函数中,对name进行了必要的检查之后,调用了create_dir在sysfs中创建目录。! f; ^- @# Z7 ^. f8 p
4 u! J; e' _: |3 P: A( m在create_dir执行完成以后会在sysfs的根目录(/sys/)建立文件夹bus。该函数的详细分析将在后面给出。
& G! L8 o; f" T
% }! O& K4 o+ f% R至此,对bus目录的建立有了简单而直观的了解。我们可以看出kset其实就是表示一个文件夹,而kset本身也含有一个kobject,而该kobject的name字段即为该目录的名字,本例中为bus。9 B6 g7 {5 r' A" l' N
4 i, |$ O6 \+ m$ j4. driver model$ i; \1 R j) k+ u5 M* j1 E' _- p
第2节所介绍的是最底层,最核心的内容。下面开始将描述较为高层的内容。+ Q0 H% [# e/ I. ], w$ a
7 E4 J& o2 h6 z
Linux设备模型使用了三个数据结构分别来描述总线、设备和驱动。所有的设备和对应的驱动都必须挂载在某一个总线上,通过总线,可以绑定设备和驱动。 }! s; [7 f) D0 Q' B# o8 Q
+ K1 U, Z9 l) z7 h' \1 \# z$ m这个属于分离的思想,将设备和驱动分开管理。) I6 ~7 D7 D% O
; }9 s# N% q& A
同时驱动程序可以了解到所有它所支持的设备,同样的,设备也能知道它对应驱动程序。
1 P: H; _3 R3 [5 m1 l5 F
0 E6 Y; ~4 ]$ b% b" E0 S4 J% @' z4.1 bus
. U2 n7 b' ?2 y, [: Y3 x总线是处理器与一个设备或者多个设备之间的通道。在设备模型中,所有的设备都挂载在某一个总线上。总线使用struct bus_type来表述。
2 C5 @, u& j; M( \; t6 i. A" t
- R: p* I. j, h& I6 z+ N1 L; N下列代码位于include/linux/device.h。$ _; n0 h6 e/ Q: o
+ T1 W/ b% |$ p: Z4 M8 v9 ~5 G5 ^# ]/ ~7 B7 B
struct bus_type {
( d2 \5 B. I1 {8 p5 u$ v X const char *name;+ z& [+ Y, _: c& w
struct bus_attribute *bus_attrs;5 u9 X; Z( S9 ^. L& y5 r
struct device_attribute *dev_attrs;
! t a; V; j, E0 g# W struct driver_attribute *drv_attrs; T9 }! b* ^2 {* s6 G. K. g
+ G1 G$ F: A' R5 N. d& M int (*match)(struct device *dev, struct device_driver *drv);1 x0 ^/ K& p8 D/ f% K7 z# \
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);: P) O4 [& _( Z# {
int (*probe)(struct device *dev);
5 H1 w+ d: F5 X/ @) X int (*remove)(struct device *dev);
6 t* p+ Y" `5 i- | void (*shutdown)(struct device *dev);
1 P" b5 d, Z5 j! I! J3 t& g& \. H* _2 B
int (*suspend)(struct device *dev, pm_message_t state);
, M* P/ q- Q" Q$ A/ e5 o* U4 z int (*suspend_late)(struct device *dev, pm_message_t state);9 K4 }" O1 H6 u# F8 y
int (*resume_early)(struct device *dev);
9 t+ f4 ~6 j3 b$ m; v: [ int (*resume)(struct device *dev);4 {' i8 T4 k- k8 Y3 N% l. _) I
. J8 f+ Y& x, Q1 T
struct dev_pm_ops *pm;1 m% T F+ ?3 s- O6 z) l+ D
% F; X. _. h$ f) Z. Q
struct bus_type_private *p;
5 I% J- t7 l' v" |( v};( \" Q" ]& `' a- W+ I% n+ K5 l q
0 V! v+ X8 y* |: k% n
/**/ n- M- I3 e3 Y% ?3 c, M* A% t
* struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure.
- H5 }* [5 E, I+ f9 y9 m' ]% I2 M *: c I( T: o* _# l
* @subsys - the struct kset that defines this bus. This is the main kobject9 E, k# n9 |& y% Z
* @drivers_kset - the list of drivers associated with this bus# f4 D8 d; X$ @2 E* q9 Y3 a
* @devices_kset - the list of devices associated with this bus( o' p" R$ o% x
* @klist_devices - the klist to iterate over the @devices_kset
4 t, U, y6 v: J+ j( r F * @klist_drivers - the klist to iterate over the @drivers_kset
* u9 j% D9 v2 ` P * @bus_notifier - the bus notifier list for anything that cares about things
9 ]/ a# L. C1 d8 F# s' G- A' ~ * on this bus.( X& a6 ~7 ]" P0 U' r9 [$ h
* @bus - pointer back to the struct bus_type that this structure is associated
' M1 f' ?6 t k- J3 N * with.; F& @3 E! \5 l# Z
*4 M; S9 \( {4 S: Z; p: d( j
* This structure is the one that is the actual kobject allowing struct
0 T7 j) Z8 K. I4 q4 i4 b. | * bus_type to be statically allocated safely. Nothing outside of the driver& T0 K L8 g! t" A7 j! I0 Q8 F
* core should ever touch these fields.
, J" [- @0 I( n+ u */
2 W* D+ f/ h3 K$ C$ ?2 }. t- Bstruct bus_type_private {
9 t( n5 K6 c+ |7 U. s struct kset subsys;
9 \3 k) _& S1 m/ B struct kset *drivers_kset;& |2 s$ p. M2 K) J
struct kset *devices_kset;
! }- z8 K( ~) n* T8 T struct klist klist_devices;
- e# l* {- z- U+ Z8 i+ m struct klist klist_drivers;) w- ~. r4 L J7 y( {3 f% c
struct blocking_notifier_head bus_notifier;
% t) o0 o9 C# N J0 W s, s unsigned int drivers_autoprobe:1;# K: e! Q, t4 ?3 X9 d
struct bus_type *bus;
) |) X3 [. A2 r& U};
; ?* J i+ t2 s& D" }我们看到每个bus_type都包含一个kset对象subsys,该kset在/sys/bus/目录下有着对应的一个目录,目录名即为字段name。后面我们将看到platform总线的建立。5 @* D( ^5 v9 v
drivers_kset和devices_kset对应着两个目录,该两个目录下将包含该总线上的设备和相应的驱动程序。& Q" } F2 p8 c2 B
) A5 g/ @* _9 T* v9 L2 v; W/ o- ~
同时总线上的设备和驱动将分别保存在两个链表中:klist_devices和klist_drivers。& o+ K9 [) b5 F. d4 J, k
2 f ~9 ^9 e3 |0 M5 {; \9 @' e9 q4 q; ?
4.2 device
$ W$ k" Q8 v* @" Q设备对象在driver-model中使用struct device来表示。
c4 C4 h( e8 E! E# o! B8 [+ }8 w3 q7 }. w& E0 e3 B" G5 a
下列代码位于include/linux/device.h。+ h. Y" K6 t8 q8 ^
struct device {
) y" a: B% ~9 |2 y$ Q4 N struct device *parent;
! N" o% _4 N4 A/ K S+ h7 _' H" F2 z t6 T4 v1 w1 H9 R
struct device_private *p;: G$ o0 h& u5 {" b' _% H' T
# {4 @8 [0 u5 V* o3 U. d8 j2 @& P struct kobject kobj;
( {$ @; C# T& {1 r/ ?5 E* z const char *init_name; /* initial name of the device */
- E: @+ b3 e7 y struct device_type *type;
( c, K+ M$ V! `8 F& b a8 |( s1 s- _2 C d
struct semaphore sem; /* semaphore to synchronize calls to* u+ L1 Q1 `. n! S
* its driver.
8 A" m1 i- W) N/ j: b/ p: }9 r */& T# m7 @7 a' v+ h9 Z) P0 j
: ^* h0 y9 X/ w+ a% n: R; U d0 h
struct bus_type *bus; /* type of bus device is on */
+ h' Z$ ^* B3 S2 d0 I struct device_driver *driver; /* which driver has allocated this b6 f% H8 ?0 K: P( I; f& {
device */, P2 c( I0 g! d6 G
void *driver_data; /* data private to the driver */
; X8 {& ^; a' y5 A) n void *platform_data; /* Platform specific data, device0 ]1 _) A; d/ v* o: g
core doesn't touch it */' F, O m5 n+ J
struct dev_pm_info power;" l5 M; V+ j, v3 W3 o
8 k3 ^8 o. \" v% s
#ifdef CONFIG_NUMA5 {6 [# H' ] N: C, p% ?; t
int numa_node; /* NUMA node this device is close to */0 N/ u2 w0 P+ q9 k! n
#endif& I. C4 l+ L( Z9 P. T+ r7 S) R
u64 *dma_mask; /* dma mask (if dma'able device) */9 k8 F1 x# u! O+ u/ a5 Z
u64 coherent_dma_mask;/* Like dma_mask, but for
9 Q9 b0 |) V( ?: u* U7 s6 l alloc_coherent mappings as" Y2 S3 T# g7 w% K3 M2 F2 `
not all hardware supports7 V2 @7 T# O# V! j; n; y
64 bit addresses for consistent% a" k0 n3 O/ s* o/ u: c
allocations such descriptors. */ R: ?- M0 X# S3 V0 n+ K0 z2 |
" v+ ?' V. \/ I$ z
struct device_dma_parameters *dma_pARMs;% A! R2 G4 K n8 b
! t, i# j: [8 H1 B$ o' \& H: a( V
struct list_head dma_pools; /* dma pools (if dma'ble) */
/ B4 k. Q$ | a/ i+ h2 G7 m* \
" |" `1 k1 W, c$ A struct dma_coherent_mem *dma_mem; /* internal for coherent mem0 U8 W% j2 i$ Y
override */
r7 D: p; `9 h6 O: F+ i0 O2 S /* arch specific additions */% P7 k; `) L4 [+ M+ ?( g. I: D
struct dev_archdata archdata;' ?1 {& X% D$ A% E- E
) |1 x$ c3 P' r2 Y. h dev_t devt; /* dev_t, creates the sysfs "dev" */7 m2 Q! a" `4 l. ^" E% ^% V
" k% _' S7 v' Z7 [8 y( L @
spinlock_t devres_lock;" c0 C8 k# s+ o2 o8 u
struct list_head devres_head;
# w6 u) S: }& q# G5 B, W
5 m# z# C: {% C: D+ ~ struct klist_node knode_class;
* s% y' G5 i% V. {: y: ` struct class *class;
# v) n5 f7 J, w( C& v- C struct attribute_group **groups; /* optional groups */) O- Q2 t9 q7 R
0 ]/ _& y2 m/ Q3 w. u& H void (*release)(struct device *dev);" h+ Q5 f& V" K: H% M
};, C* Y, w. |8 @( P" z( y
3 t4 [& F- J. B- }6 y/**
" }. w1 ?6 l: n* a* v * struct device_private - structure to hold the private to the driver core portions of the device structure." Y( s3 S- l# v6 R
*
7 K/ V8 ^" w2 l0 U$ p7 B, y* f * @klist_children - klist containing all children of this device3 f0 v- K a: L6 d* x
* @knode_parent - node in sibling list6 H4 i) `% R4 K% H
* @knode_driver - node in driver list: h; H% p8 g+ U+ D
* @knode_bus - node in bus list) T9 f" |3 k1 w/ b. k" H- `
* @device - pointer back to the struct class that this structure is
3 C8 p* H+ y" x" t) e0 C * associated with." t! Q) z1 b+ ^6 \
*
- k3 g6 _- d+ z: ^+ }1 K- H: t& P% v* m * Nothing outside of the driver core should ever touch these fields.9 ~5 `3 ?! S% H3 P9 a d) K
*/
. F) P) d3 U* r/ }" f# n8 ~struct device_private {6 \7 I( k& h* M, s
struct klist klist_children;
% K |: f8 M3 i struct klist_node knode_parent;
4 F9 w! p2 p% O' g* k struct klist_node knode_driver;
+ d# w+ ~! p' O4 F: e) I. q struct klist_node knode_bus; a. i+ r( z9 I: h% F9 a7 k. T- z" n
struct device *device;) s: Y& U& J$ J# k( H# y; f: Q
};
& r9 v2 _, V, h+ ]: ^device本身包含一个kobject,也就是说这个device在sysfs的某个地方有着一个对应的目录。& w0 J% p8 \; D8 Q. W; o4 b# r
. [' p( |- J- s* S: O0 _8 j2 ]
该device所挂载的bus由knode_bus指定。+ b) h) i' p+ u; ^ B9 b
6 J! `/ S% P& w, x4 P. `3 K该device所对应的设备驱动由knode_driver指定。6 J/ h3 y$ n0 f6 [ {+ y
' F, k' S8 H% A" i+ J4.3 driver: q+ Q4 F% ^9 U# r
设备设备对象在driver-model中使用struct device_driver来表示。( I3 G' d8 D# J( Q! K
7 u# D, A" M$ Y' F7 _7 A9 l& `% l9 l
下列代码位于include/linux/device.h。' u) x7 ]/ ], U. x( c, Y
struct device_driver {8 G, _+ W# p9 J, z+ W" D
const char *name;
$ a, k0 v3 g- ]6 \0 `4 e+ g struct bus_type *bus;
/ Q& U% R+ \! l, p: s Q1 P* `; G9 {5 s) \: t( Y' ?
struct module *owner;
7 S y, A$ [3 j& O E9 K7 r const char *mod_name; /* used for built-in modules */: y0 @# l# X* D& X0 f
! l' s+ q1 {2 L
int (*probe) (struct device *dev);
& L1 h5 s! } m6 E- t6 | int (*remove) (struct device *dev);
1 s# R; P- M+ Z8 x; [) j void (*shutdown) (struct device *dev);
" r3 G3 e; i+ T: J" s6 A/ m+ Y) V. h int (*suspend) (struct device *dev, pm_message_t state);
; _8 W/ X, O: a6 n# a, Q6 T int (*resume) (struct device *dev);% z( [6 W8 W' T- \' G! ` |
struct attribute_group **groups;+ s( i: x% H& R# t
+ q% b5 S( K/ L$ N0 `
struct dev_pm_ops *pm;
7 ?9 ?9 K) e6 z$ [3 {, \; X
0 u2 f z6 A5 Z7 a; I; A( ?* y% X0 P struct driver_private *p;' `* g3 ?0 G2 `' U2 ?; W+ u
};
; F3 z4 y) Y! S7 a5 f! r; @; h3 m
struct driver_private {
U" u; U; O$ Y7 ? struct kobject kobj;
1 m" b4 z8 K$ ? struct klist klist_devices;$ }4 [! `* h0 N+ U
struct klist_node knode_bus;. G1 K+ \# x* d% P" L& q
struct module_kobject *mkobj;. ?; w0 S3 V" I7 w5 \
struct device_driver *driver;
: t+ C1 W1 ^" }# j# J; {};
, C0 m2 D$ {( t+ J }device_driver本身包含一个kobject,也就是说这个device_driver在sysfs的某个地方有着一个对应的目录。
# {, J% n6 p7 Y, I0 u4 G7 V该设备驱动所支持的设备由klist_devices指定。) A+ o7 M2 ]0 ^0 ?8 p% O! S
. ^; n; m$ d4 W
该设备驱动所挂载的总线由knode_bus制定。
5 S* }0 i& G, \( k3 M5 ]3 E; w" P$ |
5. Bus举例
6 }$ x4 [( k6 h5 D& }& c本节我们将以platform总线为例,来看看,/sys/bus/platform是如何建立的。
" ?, b- i6 [& M7 R+ i# `
H; u/ @; _+ D+ m) F3 k9 Hplatform总线的注册是由platform_bus_init函数完成的。该函数在内核启动阶段被调用,我们来简单看下调用过程:
2 ^! R& h: y$ J7 S! H$ Y- A0 |& s' s5 f7 N1 v
start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。2 a- n' A1 b) b/ r7 B
6 Q- O; Q. c' U$ T7 g
注:kernel_init()是在rest_init函数中创建内核线程来执行的。
; y8 `) ]+ K+ Y3 [, ^
0 w& t4 U; D' s; ?1 o% k
5 M* r$ s- v1 z
+ X Y6 O/ k9 ^1 N/ bint __init platform_bus_init(void)8 ?/ A9 _! i2 C- c
{: {7 r; c6 \. s# O" R0 g$ V% m
int error;/ G8 X: v6 T, L& b
# R- L0 w5 Q- _2 u+ S4 c. J
early_platform_cleanup();" Z) {/ T. Q% ?0 q& G0 J. H
8 o9 _( p9 z" L5 b- M4 B error = device_register(&platform_bus);
: b( ?+ L3 J" ^7 V8 F if (error)5 b7 q! d. U% o' [0 p b
return error;
/ T4 W8 C. x) D/ l8 c! u$ H error = bus_register(&platform_bus_type);
! a5 [; ?& ?, i" G7 F if (error)
' K* N+ ?8 f4 N( C device_unregister(&platform_bus);
# B( D. d3 h. i* d return error;' c) l. ~: `' C7 E
}" v2 K3 x0 R& q N# l* ~( A
struct bus_type platform_bus_type = {
( s4 W. j9 a2 V/ l8 g8 u .name = "platform",
" ?( X+ T: [/ z' @7 a .dev_attrs = platform_dev_attrs,
% p: P: v5 b- F .match = platform_match,' y) {$ G8 S: f( o) @ ]) k
.uevent = platform_uevent,8 b3 \$ J9 r+ T
.pm = PLATFORM_PM_OPS_PTR,4 z# S5 ~4 T6 y/ e/ A
};) l6 T c3 l: {5 }9 _4 r5 |( l
EXPORT_SYMBOL_GPL(platform_bus_type);
0 u9 T; ?# a* }' R( N/ l9 z& b从bus_type,我们看到该总线的名字为platform。3 L5 M7 g, G& S0 z% I; t, w+ J
调用了两个函数,我们只关注bus_register函数。
) T E8 }1 H1 u' J$ n# j- _9 G5 t; R# x( p+ q' R: C* W6 \9 c7 f
! I8 E+ f6 D: c* `+ s2 Z6 N/**- ^! ^/ A! I9 T
* bus_register - register a bus with the system.
0 T/ ], X0 n2 x6 ]9 _# R( s * @bus: bus.1 P- o. Q' H' B" M; T
*% _# [& F" d# \& _3 M5 ~& b) c- ]
* Once we have that, we registered the bus with the kobject
5 m1 y$ R& t$ R: ?; V3 t# v3 [7 z * infrastructure, then register the children subsystems it has:
, o9 v X/ U5 W1 H, ]6 n * the devices and drivers that belong to the bus.
! D" ?3 Z, }# @0 k2 M$ B */9 }8 Z3 o* z4 [* ?
int bus_register(struct bus_type *bus)5 e2 K' J* z5 l# s
{
" ~- u! O1 Y! G6 q0 ?3 t3 [ int retval;
, i$ f/ J) L4 d struct bus_type_private *priv;
' k! O6 z2 j' |* @( V
' s# \# c9 `' r$ V1 C priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);. C( F( [. _8 R6 _6 h% d
if (!priv), B" ] p7 v. x' Z1 i) d3 z- |) E
return -ENOMEM;2 f$ s( d8 Y$ a2 X- E, Q
/*互相保存*/3 Y4 e3 L$ O; b. M& V$ I8 H5 K
priv->bus = bus;
3 o& T+ j8 z5 n8 O$ Q bus->p = priv;0 N, s! }/ h3 g6 |( ~
5 p$ {! r+ K% W! y) c BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);% F( b& L9 E" u: I/ @( ^, c
/*设定kobject->name*/
* r1 g0 j( {/ W4 g retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
( U. Y$ v; I$ s5 h7 V) h1 g5 n0 s if (retval)
& F) g! k6 B2 d; j( n goto out;
2 c! F6 C/ I! K8 ^: r* T5 O' k" y2 I2 M1 A$ K
priv->subsys.kobj.kset = bus_kset;
- I/ A D# f, M priv->subsys.kobj.ktype = &bus_ktype;( ]. s1 }. _& M. ~1 p+ _; {6 x) D$ H
priv->drivers_autoprobe = 1;
% s! p2 L- Q4 f7 `1 {) q3 w5 T
7 G: X4 D9 y& w3 v) G3 G& ^ /*注册kset,在bus/建立目录XXX,XXX为bus->name*/
# z9 j3 E" w2 r: d- e3 B. v# M retval = kset_register(&priv->subsys); 4 e- A# r6 b& r8 D
if (retval)
4 k2 O2 E1 B% ], q* s goto out;
" A. [5 |- d" Z P5 f
$ L" j6 o8 Q% A- t( o* j0 W /*创建属性,在bus/XXX/建立文件uevent*/# y6 Q" {. S, w
retval = bus_create_file(bus, &bus_attr_uevent);
: \: p! v% |- | e# K: E" T if (retval)
$ {" `& m! ^6 a9 F4 }2 P9 [ goto bus_uevent_fail;
0 l2 h" ~+ d% @; }' U* g( E* a7 `' }4 O
/*创建kset,在bus/XXX/建立目录devices*/
6 U( o- E7 Z- D/ S: _5 V. |3 z; w priv->devices_kset = kset_create_and_add("devices", NULL,
. b0 k7 \. N6 a/ e% b' t) G- h! m& P &priv->subsys.kobj);
- `# @) M4 F5 w" e' A+ F if (!priv->devices_kset) {
( t4 v% ?- V/ ?. Z* x* I retval = -ENOMEM;
( X; t2 X% H5 p! K/ ?2 f goto bus_devices_fail;: y9 ^3 q2 ?/ N6 q- M
}
' h. ~% u, Q j/ J. u* D% G0 Y8 J. t" h2 p' ]$ u
/*创建kset,在bus/XXX/建立目录drivers*/0 R5 k( x, Q: o( [
priv->drivers_kset = kset_create_and_add("drivers", NULL,
: h7 q- P4 ], \4 `" ?! }, b$ ^ &priv->subsys.kobj);4 `2 Y, `; }% T8 z h9 w
if (!priv->drivers_kset) {7 N2 m# [% h0 _+ k5 K9 ?- @
retval = -ENOMEM; }8 P. T9 H( X$ j& W/ l
goto bus_drivers_fail;1 y5 w5 v1 }6 z# [
}
2 y3 G! `$ P" G8 P2 f /*初始化2个内核链表,*/! |! I% o! G" v3 b' _; C
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);7 [7 v# s" C% b; a0 L; y" u
klist_init(&priv->klist_drivers, NULL, NULL);5 x1 w* O& }% t
( T: y1 {% t& q h2 D! M7 g- v0 m /*创建属性,在bus/XXX/建立文件drivers_autoprobe和drivers_probe*/% A- N! D, ]$ z2 H3 y5 h+ P8 R
retval = add_probe_files(bus);
: g& V" j2 C- }( P! }! V# E, R5 E if (retval)
# h, N! O9 r. G# e- {5 u* {6 y9 @ goto bus_probe_files_fail;
9 f; s6 P: ]% X7 D; | /*根据bus->bus_attribute创建属性,在bus/XXX/下建立相应的文件d*/" d4 r% c* T$ B/ ^2 u% U
retval = bus_add_attrs(bus);
( f! L/ ^: @) p$ J if (retval)
. ^; t# A# r! ^( F6 G7 q goto bus_attrs_fail;7 Q9 F* j( K4 V% Y* r
; [8 i& b2 H/ |7 x0 N* M7 S$ | pr_debug("bus: '%s': registered\n", bus->name);
3 I: P3 \+ L3 T8 |+ u' r return 0;
/ h' W( ]: T3 R; F8 h
' L% W) p1 E/ Y! C; _1 |bus_attrs_fail:* @# T" K, C+ R4 Z1 Z/ w, \2 S
remove_probe_files(bus);
# c1 I, H, ^# O1 pbus_probe_files_fail:
& [+ F% M w/ e, g. W( m# } kset_unregister(bus->p->drivers_kset);4 d; {' s4 Q( w
bus_drivers_fail:
+ W3 K6 W" W# v8 r5 D ~' B8 S kset_unregister(bus->p->devices_kset);6 j- k& z, C- D+ ?
bus_devices_fail: l" i2 O8 Z% H, i) F
bus_remove_file(bus, &bus_attr_uevent);6 Q7 z: g0 X7 _7 p0 b/ J# c: Y- I
bus_uevent_fail:6 G+ _1 i, g5 P D+ z6 t
kset_unregister(&bus->p->subsys);* V$ X) l1 [* [" T3 a; y
kfree(bus->p);
% e5 J4 I# k5 Bout:! ~" y: ?6 c' y/ t
bus->p = NULL;
$ K6 u. k5 ~" q2 t$ P H return retval;
7 O& b& E; `8 l% E8 z0 w}5 C$ @! Y% V+ H3 Y+ i/ F
EXPORT_SYMBOL_GPL(bus_register);
8 `: f+ B0 v2 v/ S4 }# S) t! ^- Y) `7 }
函数中,首先调用kobject_set_name设置了bus对象的subsys.kobject->name 为 platform,也就是说会建立一个名为platform的目录。kobject_set_name函数在3.1小节中已经给出。
$ s$ j+ t! q) Q! V在这里还用到了bus_kset这个变量,这个变量就是在第3节buses_init函数中建立bus目录所对应的kset对象。
7 n8 t, F. _+ |% \
, @" H S# r& c: I7 F接着,priv->subsys.kobj.kset = bus_kset,设置subsys的kobj在bus_kset对象包含的集合中,也就是说bus目录下将包含subsys对象所对应的目录,即platform。
; V2 j& t9 m& F% r1 h5 A! N) `( f! M- |* q' S0 U
紧接着调用了kset_register,参数为&priv->subsys。该函数在3.2节中以给出。在该函数的调用过程中,将调用kobj_kset_join函数,该函数将kobject添加到kobject->kset的链表中。2 l% E7 i# i: A& V
7 B3 b6 o$ K' m. V
6 g& Z) z6 ]0 p% g
/* add the kobject to its kset's list */
0 y! C7 Q: z$ ` `& k$ T. P+ \static void kobj_kset_join(struct kobject *kobj)
; u/ l, V" A( P' [: F1 H{- z. P" S7 \* ]# n3 k
if (!kobj->kset)
/ j3 M% p1 N& |* }. y, @3 W return;, \( Z! x. O. A$ [1 i
# l+ O8 Q/ {/ L5 W6 [0 j9 } kset_get(kobj->kset); /*增加kset引用计数*/
' D$ I" u" z, }8 y spin_lock(&kobj->kset->list_lock);0 p+ a( ^. v6 q* P
list_add_tail(&kobj->entry, &kobj->kset->list); /*将kojbect添加到kset结构中的链表当中*/
. b" X1 V6 G7 R5 q2 f; Z! u; @ spin_unlock(&kobj->kset->list_lock);* |. R: n5 I1 S! F- j
}
" m9 \' |' y% o, o2 J. Skset_register函数执行完成后,将在/sys/bus/下建立目录platform。此刻,我们先来看下kset和kobject之间的关系。
1 P% i- Y& p' d5 B; q: S; b, W3 v+ n% H3 B) `( f3 J
& S4 K3 a' P6 E4 K* w/ v
3 a3 r1 t$ x" l5 i2 f5 z然后,调用了bus_create_file函数在/sys/bus/platform/下建立文件uevent。/ n& n" h+ @3 r* |, I1 |0 E8 M
" N7 V/ l7 M7 W: Y' @6 m Hint bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
% a% n5 Q9 H Y) h+ e5 e{6 k. _$ |/ Z+ u! P; m
int error;
% G& h" L' Y0 ?% ^- r if (bus_get(bus)) {
; M: ?+ }) [* x; ^' k$ Z/ K: u error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
* l7 G) x9 C) `3 u3 D bus_put(bus);
: M" D) {/ h9 a- A( k2 }! s } else
+ ~& g. d9 z" i error = -EINVAL;- \4 d7 ]% C8 s/ M
return error;( [! u. [) O9 D5 E
}9 z7 R6 D* F$ }# n6 |; _( ?
EXPORT_SYMBOL_GPL(bus_create_file);
B0 P6 D! D' W$ Q7 O* I) f$ d有关底层的sysfs将在后面叙述,这里只要关注参数&bus->p->subsys.kobj,表示在该kset下建立文件,也就是platform下建立。
0 Z) G& o* s9 @ f接着调用了2次kset_create_and_add,分别在/sys/bus/platform/下建立了文件夹devices和drivers。该函数位于第3节开始处。
T# w( d( d6 Z8 Y
9 X1 U' s- W$ h这里和第3节调用kset_create_and_add时的最主要一个区别就是:此时的parent参数不为NULL,而是&priv->subsys.kobj。9 M- B$ v X: q Z9 s5 A( U
# h: ~' {2 y0 s) _/ E2 u
也就是说,将要创建的kset的kobject->parent = &priv->subsys.kobj,也即新建的kset被包含在platform文件夹对应的kset中。
; |1 b4 G+ J* a: N3 m+ Z5 R, G: }3 }. J7 S& r9 t
我们来看下关系图:$ u) W8 s/ p6 s$ N9 ^
& V4 X" h& M4 b: U% ^( m3 w( X
& S. o: T; F) H7 [! r
, k0 F6 {. f. e( }随后,调用了add_probe_files创建了属性文件drivers_autoprobe和drivers_probe。
# x" o1 E g( D. J2 V% V- ]6 r# T" V" i' f/ U) `
static int add_probe_files(struct bus_type *bus)* c' v+ \, U% Y( D" N
{2 d8 _& E# b i- U* s- `$ ]5 [
int retval;
+ t. D- S# t: _6 ~0 b
/ a- V$ Y9 }6 @( b6 [ retval = bus_create_file(bus, &bus_attr_drivers_probe);
% I8 v8 l% O$ ?. R; B if (retval)4 Q- W# _- b; I
goto out;$ g: M% r9 [" _' B& A
5 s' A- H3 k x retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);5 h1 ?+ ?. Y4 C' C6 s: K t
if (retval)
" g, h) h6 M( x; f2 L$ O bus_remove_file(bus, &bus_attr_drivers_probe);, r+ D r5 k8 K
out:
- `8 ?$ z- R8 J3 v, t return retval;% s7 s% d8 J6 |$ [/ Z s) C9 M5 ]
}6 Z* Q$ M# @# R* {$ I5 l
该函数只是简单的调用了两次bus_create_file,该函数已在前面叙述过。# y1 O1 d" V) P% e
最后调用bus_add_attrs创建总线相关的属性文件。
i! R2 a q# ~; n; Q2 e* x L: p. z$ \; M2 G: e
/**+ C9 W3 _' u2 K, L# s6 |2 v
* bus_add_attrs - Add default attributes for this bus.
" N7 M* P0 B" _& o0 e6 @( u( e * @bus: Bus that has just been registered.! r2 e |- v6 y
*/
. i: I* u/ X1 K% j0 Q: V' c
1 H4 K: G: {; S; {% Estatic int bus_add_attrs(struct bus_type *bus). Q( b. @! W# Z! ~
{$ I9 n& c" j9 C0 t
int error = 0;
3 U1 R! W3 s s# u; H3 c1 ] int i;
' V K1 N& d4 t, n7 b- c. p9 `3 f0 Z: g8 a# a8 s
if (bus->bus_attrs) {1 r4 N/ u& S7 g) ]- W. Y; g& j4 w
for (i = 0; attr_name(bus->bus_attrs); i++) {
, d+ o' D1 A: M: _: v; N4 r error = bus_create_file(bus, &bus->bus_attrs);
% n; F, L# H5 T5 @: j# ?! M C if (error)
5 c3 |# z' \- }4 S4 c+ M goto err;2 N7 E9 @, s: f% }2 s
}) f+ l; X+ A- i8 {! G
}% `3 X* L; S0 e: K
done:
: ?2 G* H$ p( E' J+ T5 O return error;
! z% L& y* E8 P' Oerr:
1 }, t- {! Q6 B9 p. H# G# o% \ while (--i >= 0)2 z; a* S% Z& V5 W, |! C
bus_remove_file(bus, &bus->bus_attrs);
$ w& R3 B9 l8 k n8 a8 z goto done;" p' o7 e. X( a
}$ b; e7 A$ N- @* a9 l: ]# N6 q! Z
我们可以看到这个函数将根据bus_type->bus_arrts来创建属性文件。不过,在本例中,bus_arrts从未给出定义,因此次函数不做任何工作。 K! `$ \6 @" \) i- y3 r" `
好了,整个bus_register调用完成了,我们来看下sysfs中实际的情况。& `# S6 N' f, A& ^
! n8 F3 C. Z p3 ~[root@yj423 platform]#pwd6 t) G) T; b1 c9 X/ G; T* p
/sys/bus/platform
7 i, T0 }; a( f' {* ?+ t3 Y+ z[root@yj423 platform]#ls
3 h% b) \; g, e% L) o0 |) ?devices drivers drivers_autoprobe drivers_probe uevent* m; R/ ^3 P1 p
最后,我们对整个bus_register的过程进行一个小结。
: g& Y% v( N0 h- v/ i% b' K
8 l1 F) g( X5 k$ c- s
& |5 t! c9 H3 o9 R! E
1 p, v' a! j, t* m3 T3 e6. device举例
+ |+ c) x: d* ~1 m本节将首先讲述如何在/sys/devices下建立虚拟的platform设备,然后再讲述如何在/sys/devices/platform/下建立子设备。3 l s: [# K9 b$ _8 o9 D7 ?
% I) b/ | f$ }9 u7 N1 j+ v* q
6.1 虚拟的platform设备
) U8 s( h# c R之所以叫虚拟是因为这个platform并不代表任何实际存在的设备,但是platform将是所有具体设备的父设备。
3 d0 z, D7 O. J
( u. I% p; T% l0 _0 A! P$ }在第5节,platform_bus_init函数中还调用了device_register,现在对其做出分析。
8 ]- m/ e) x8 R9 Z: k
- s" \: ]7 m- W: r; `# Y2 j' f/ cint __init platform_bus_init(void)
; @ ^6 C9 X" d4 l{
' _' p9 W3 s* Z7 u+ y int error;
/ u. |4 c1 K) ?# w6 L" y
" q' D) p! J9 p! t2 L early_platform_cleanup();2 i2 W0 i9 s A' C, k
& H' A# J* `3 _* ?- V1 \5 }
error = device_register(&platform_bus);9 Z9 e6 Q4 ]4 h+ w' Z/ v
if (error)
& s# W4 I# l) _* B- h# u return error;
, R4 F; K- I( d4 T. R error = bus_register(&platform_bus_type);$ Y; @6 G( [8 }6 j. |6 A1 k
if (error)# M7 Z9 L3 Y( F5 f* ?2 B
device_unregister(&platform_bus);
" I% q+ z3 F& d; a# O return error;9 u4 _2 |% |2 o, R0 @- f3 U
}
* g6 t) t( O/ A# q# B8 Z+ y2 V; v6 q" }3 ~3 Z3 J
struct device platform_bus = {
* F1 ^* B ?. o+ Y0 S! g& H g7 ? .init_name = "platform",1 Z) f* w2 r- m8 {
};0 D* M6 \9 K, q- z6 x, J
EXPORT_SYMBOL_GPL(platform_bus); i: U; D8 Q. d) m9 u
下列函数位于drivers/base/core.c。- M5 H& v' r8 g3 g1 T* a5 J6 z
/**
( b6 A+ D/ @& o4 ?9 I- @ * device_register - register a device with the system.
# c. v9 x t0 \# c0 d * @dev: pointer to the device structure
( q% S0 V! u6 i( p *
$ C" n/ ]( X, |; b* u * This happens in two clean steps - initialize the device8 Z F# @& ~& D& a* s5 M. V
* and add it to the system. The two steps can be called
! D, y0 x" ?6 S7 E * separately, but this is the easiest and most common.
3 _$ ~* R% ^' T: m o7 I. G9 p' P * I.e. you should only call the two helpers separately if7 V$ a7 ]& M0 R! Z# z0 W
* have a clearly defined need to use and refcount the device8 i0 G( p( B' M. `* {7 p* e/ ~! G* `
* before it is added to the hierarchy.
[' D# x7 b, B- e/ r, t *
- ?, x4 I& B8 f+ \, M * NOTE: _Never_ directly free @dev after calling this function, even! C7 @' g; K$ v5 g0 P0 l/ M
* if it returned an error! Always use put_device() to give up the
. q4 f8 t7 l5 q* ?3 j9 `6 _ * reference initialized in this function instead.
; {& X. V5 G) m */
4 z4 Z, z2 u: j8 `3 m9 k, {* n9 e# Zint device_register(struct device *dev)
7 f6 n( R; [! ]9 Z( E7 a8 R) H7 ?{: U& h' P4 Q; n; [, p( x
device_initialize(dev); /*初始化dev的某些字段*/% t' ^3 q5 [3 k9 F) J/ `, j
return device_add(dev); /*将设备添加到系统中*/
# e3 F0 K' s8 U: x* N* f}
4 I( E( Q* {' Q6 q
9 Q* r7 x4 O9 }$ m9 Q7 K' y2 p' _一个设备的注册分成两部,每步通过调用一个函数函数。首先先看第一步:. M7 d* [/ @& \) m
2 x- h3 F; l8 T. [" v! X; G) L" e
下列函数位于drivers/base/core.c。
2 ^. Q4 S6 E9 s( N2 L/**
% k% a8 m1 N. b7 o" v * device_initialize - init device structure.6 R; I% q8 i0 \5 @
* @dev: device.
0 ~4 {- Z. q; Q+ q *
7 I& o$ g: Z! k p * This prepares the device for use by other layers by initializing# Y! S9 s, {; m8 u
* its fields.
2 v! B$ A" G0 ~* a * It is the first half of device_register(), if called by
/ z' I k: Y% w' v6 t, T$ d1 g1 f) U * that function, though it can also be called separately, so one9 L" r' |( k2 u5 u
* may use @dev's fields. In particular, get_device()/put_device()9 e9 o9 l. S |, J: L6 y
* may be used for reference counting of @dev after calling this7 a% D) _6 ~8 r+ C* _
* function.6 n; B& W* j m ]) x/ W
*
0 n) P( [: e: m * NOTE: Use put_device() to give up your reference instead of freeing* `3 E3 z# r _3 R
* @dev directly once you have called this function.. y: r- Q4 {; w4 Q0 K/ E
*// R2 o8 {; ~4 l0 L
void device_initialize(struct device *dev)
- B; x! P% i0 i7 b# V{" _9 s9 E. K$ r. l, P# M
dev->kobj.kset = devices_kset; /*设置kobj属于哪个kset,/sys/devices/*/( p; P M) J; Z% V2 c
kobject_init(&dev->kobj, &device_ktype);/*初始化dev->kobj*/
& v9 _( A& {" f/ p INIT_LIST_HEAD(&dev->dma_pools); /*初始化链表头*/
. u0 m+ |3 o# U init_MUTEX(&dev->sem); /*初始化互斥体*/7 `& K$ q* H0 e/ Y+ h
spin_lock_init(&dev->devres_lock); /*初始化自旋锁*/3 J+ ~" ]8 k* A. ^$ z0 g
INIT_LIST_HEAD(&dev->devres_head); /*初始化链表头*/
5 W9 A2 M( U6 T: ]: S6 ]8 H device_init_wakeup(dev, 0); /*设置该device不能唤醒*/
\& l+ o2 f' k @% t device_pm_init(dev); /*设置该device可操作*/% e& e* Q0 j# O2 u5 Y
set_dev_node(dev, -1); /*设置NUMA节点*/
1 A9 R. X, F. _: W+ |5 C}: t* @1 Q. M( S% V: k( e3 G; \
6.1.1 有关devices_kset, I$ F9 E5 ?) {# L! }2 w( L& }
首先其中用到了devices_kset对象,这个对象和第3节当中的bus_kset是同样的性质,也就是说该对象表示一个目录。
N- Y( u6 M6 a1 \* [: M
" r w+ W8 V; i) Q4 l3 R/ J5 ?. J/ P/ Z; _该对象的建立是在devices_init函数中完成的。
; a0 E8 N. @8 hint __init devices_init(void)5 [. p3 s% \+ s& ~! o
{
/ m. D3 v: n; F3 \ devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
! }. v- }/ o9 i. x Q9 J if (!devices_kset)* K5 ~' O4 K, {8 r) [, }0 E
return -ENOMEM;. Q7 Q K+ z! ?8 b9 H8 U3 ~$ ^ M
dev_kobj = kobject_create_and_add("dev", NULL);, y& T2 Z9 u, o" d( o( r
if (!dev_kobj)
$ Q' i' u: a* Z# P$ r/ D9 n goto dev_kobj_err;
$ f, x2 p/ Z! ^" E8 b) a. X: y sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
( o) G$ M7 y3 T+ O$ s) M if (!sysfs_dev_block_kobj)8 r) @# \+ A# b$ D( j+ ?
goto block_kobj_err;
l9 G7 z% s- R; \( d; k \$ h sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);, X1 D3 s' h. U+ H
if (!sysfs_dev_char_kobj)
1 k( L# a# N+ R8 s; b$ c* e. v$ x goto char_kobj_err;: l' u! z6 g6 P4 J) T1 s
, x. i j. K6 u4 s+ z! B6 }5 G
return 0;
/ k: g; ~, S( ?/ G
: v& l. v8 t: ^+ p/ w! _% @ char_kobj_err:
M! t4 U- q6 n8 ^0 S" l kobject_put(sysfs_dev_block_kobj);( ^/ H4 m! ~8 C" k; F$ y
block_kobj_err:% d2 j! l7 B( s$ z9 S
kobject_put(dev_kobj);! p/ R3 o3 G9 Z# i- e" K+ G
dev_kobj_err:; A% d& E+ a, A! Q3 s
kset_unregister(devices_kset);
- @' j- [! \& q: X' r- x, R return -ENOMEM;
' \" P* q4 a0 I! _4 V3 Q4 t}) ]5 @3 s y; B
由此可见,devices_kset对象表示的目录为/sys下的devices目录。0 f9 E( b5 A+ `6 P9 R+ T% Y. i
6.1.2 kobject_init0 N9 R P0 _% N V, Q0 U
下列函数位于lib/kojbect.c。
; d0 ~$ w8 I! m6 A/ w8 I$ p1 u; p/**, b' U& ^+ u2 O+ T4 w
* kobject_init - initialize a kobject structure+ V% n4 f$ p6 E# z* X
* @kobj: pointer to the kobject to initialize
2 Z0 L7 E& ~( z7 K * @ktype: pointer to the ktype for this kobject.1 J$ ~% L2 c9 a$ ~
*
8 {$ j) f: L. Z3 G8 h3 d+ U# i* U5 M * This function will properly initialize a kobject such that it can then% |) W" w, @/ y, o% [7 |. l
* be passed to the kobject_add() call.- c e/ H& T. b+ \/ K, J) A# i
*
( Q1 P" m K3 }8 g2 \ * After this function is called, the kobject MUST be cleaned up by a call) f" ~+ r3 e: X2 z3 M: s# N6 M+ @
* to kobject_put(), not by a call to kfree directly to ensure that all of
& O& e# Z: ~! R- H1 m * the memory is cleaned up properly., |6 ^6 w7 v( h2 b Y
*// C x) l/ {: z
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)) f1 y' ?9 {/ k9 U1 t+ q% A
{
3 M2 }& R4 N8 c9 x% F char *err_str;4 |; y8 v# y/ O+ `" w d' l2 J
" t- U; \( U- i, Y: V: ?
if (!kobj) {
) H4 C6 S: K; ^& V, w: P err_str = "invalid kobject pointer!";
7 f" |; J( k% D8 B( s! p( g goto error;
) h- G: s% U* j6 O& \; ] }, V4 T! w# x# y
if (!ktype) {1 c0 U6 |$ x) K1 b& W+ G7 \
err_str = "must have a ktype to be initialized properly!\n";
2 O. _; a. D4 X5 F goto error;
3 T+ Z9 @( |1 w4 Q }+ w* ^1 Y) [$ K& c* G9 J
if (kobj->state_initialized) {/ I% f0 A, k8 a( Q8 @% f& N
/* do not error out as sometimes we can recover */
" [& d; Y3 t& H' P7 S printk(KERN_ERR "kobject (%p): tried to init an initialized "4 N! l8 \9 _% {% o* |' W. p
"object, something is seriously wrong.\n", kobj);$ |5 p9 |1 f% I' L& J* i
dump_stack();
- e6 Q# F b8 ^5 L. ? }* m. j- c6 V9 j% h# u: Z
]: N" P4 T. Y
kobject_init_internal(kobj);0 }% D4 C( w2 _' x+ Y
kobj->ktype = ktype;
* }9 E2 d. s: K return;
: B, z/ g2 L8 {# b- ?
5 ?! a2 m7 p @9 eerror:+ d$ K! ^3 i5 `' Q
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
% k" I5 |% W/ Z" j$ L dump_stack();5 f# e. g6 y; M& _
}' ]5 a# q1 e$ ?9 k0 h3 R7 ]
EXPORT_SYMBOL(kobject_init);
3 p: d; P$ O3 Z2 N( | T' } e- z. G1 m
static void kobject_init_internal(struct kobject *kobj)
2 ]- N9 O I8 u% o8 X, L U0 |{% C* E9 `1 q h$ H8 s* k
if (!kobj)+ E5 _5 V+ }9 t# m3 I( m
return;) F2 m% r2 H7 l# R( X% Z* A
kref_init(&kobj->kref); /*初始化引用基计数*/
; k# H; Y2 E! q S5 P8 n ^: X INIT_LIST_HEAD(&kobj->entry); /*初始化链表头*/' S* z# K! S/ C$ [ V
kobj->state_in_sysfs = 0;$ J/ H8 k* ^2 p2 U- b) n- P
kobj->state_add_uevent_sent = 0;
( M9 C+ ]5 L- \% e/ i- T3 [: y kobj->state_remove_uevent_sent = 0;
& z7 A8 h' ]: L q kobj->state_initialized = 1;- n8 ? c/ R* ^# F1 o1 J) N$ ~
}
' \3 B D' q+ E- h/ v. e0 i% @2 }+ x该函数在做了一系列的必要检查后,调用kobject_init_internal初始化了kobject的某些字段。
; r/ y0 p6 K/ t0 o6.1.3 device_init_wakeup
6 j q5 t; k2 w0 B& e参数val为0,设置该device不能够唤醒。9 j& [1 `% ?( ^4 F/ t2 @
#ifdef CONFIG_PM
5 Y9 [' `- x1 \; C6 R) K/ C1 U
f* |6 ^# _) ]' g. M/* changes to device_may_wakeup take effect on the next pm state change., w7 O) N0 K' `+ E
* by default, devices should wakeup if they can.; }7 P& r N: W7 L2 J' b" _
*/9 a6 Q+ B* L, s
static inline void device_init_wakeup(struct device *dev, int val); V+ X8 S; `- j" f' o
{4 q2 C: c( r8 w1 w: i
dev->power.can_wakeup = dev->power.should_wakeup = !!val;- h* C$ v8 Y d
}' q& W6 q2 D& Q6 r; l8 F
。。。。。。8 I! c8 M8 W$ j) o
#else /* !CONFIG_PM */, P3 R9 [- o3 ^5 X W1 `2 o+ I- ^3 j
5 G) p+ ]. x5 c" `/ E* r
/* For some reason the next two routines work even without CONFIG_PM */" h+ C2 ?$ ^7 a5 k8 O- _* ~/ H! g
static inline void device_init_wakeup(struct device *dev, int val)
' h! C8 Q$ A# o* I/ K{1 i9 h7 O1 ^" p3 W& Y
dev->power.can_wakeup = !!val;
9 j( y P4 r3 U" D/ H1 H+ ]. r}
3 i/ b# C7 p- U& a。。。。。。
4 l$ q, m0 M- ?4 t+ f, i. H#endif
2 m. z+ F$ @1 ?6 G
: G8 m. N& g. `% w$ V6 x0 s
6 E- ~. \$ S3 [2 {0 H$ F$ s6.1.4 device_pm_init# {+ t) v; W: B1 k( r
设置电源的状态。
* e2 d/ U5 S8 \& k" Y Xstatic inline void device_pm_init(struct device *dev)
4 i3 e' e1 N; U{
$ d8 ?# z& Y3 N+ K# X( W dev->power.status = DPM_ON; /*该device被认为可操作*/ g7 O5 u; r, l% p. n" ]
}
. m3 {7 u8 M0 u3 X; `; o1 R. M9 h4 g6.1.5 set_dev_node& X" F6 ^7 D' s7 J$ r* x
如果使用NUMA,则设置NUMA节点。
/ e! @9 G' p- S' `! Y#ifdef CONFIG_NUMA/ m0 A0 J: k0 Q/ y
。。。。。。, x! G4 ~+ d. R# f% k4 C% n8 T1 h
static inline void set_dev_node(struct device *dev, int node)% P8 |0 ?+ N# X8 h
{
9 m) G" U: w9 ?5 m dev->numa_node = node;
; }% R( q& E; k: A- v) g8 K1 @+ a}8 _: ` V# w0 D, V6 g; y! w
#else0 T+ B/ i/ p$ g( u, k
。。。。。。
! Q2 i2 [2 ?& N* ~! j9 Sstatic inline void set_dev_node(struct device *dev, int node)% |6 l" B0 S- u( f, v/ R
{
% e% t: M* O" ~}
/ q/ r, U2 r0 m: e#endif
) A; o" ^9 D! K
$ T% j: G) {1 b3 ~. @1 ~' p, B6.2 device_add
) t9 {: p& i: w$ h. ~: w接下来是注册的第二步:调用device_add。
# }; m: v: Y/ t1 S' i7 ?7 ~; ~1 v/ }4 D+ S+ ~4 |# O* W k7 V
/**2 |, O& Q7 H) _" u1 l, I
* device_add - add device to device hierarchy.
9 U: z ?+ {+ ]" n7 r * @dev: device.
% S" N3 G( X; U% L *
6 a f6 b- m1 z1 {" Y' x * This is part 2 of device_register(), though may be called; f7 H& d: C0 z# A( ~# D: V8 M+ R3 \, Z
* separately _iff_ device_initialize() has been called separately.* g8 }: O0 z6 W" y( k+ @
*
' j- C5 A' t2 i p8 {- Y$ \2 g * This adds @dev to the kobject hierarchy via kobject_add(), adds it V/ {8 ?/ N0 s: b
* to the global and sibling lists for the device, then: `. O5 G0 s0 i4 p7 f5 q
* adds it to the other relevant subsystems of the driver model.
& |" e! y- Q8 V* d. s *# t! L( L* L1 r0 {7 ~
* NOTE: _Never_ directly free @dev after calling this function, even8 Q" C- M. n/ g2 a
* if it returned an error! Always use put_device() to give up your; w7 e- J* ^/ G: V; ?
* reference instead.+ x1 E e1 {/ n7 k7 f R
*/7 B( r$ A/ Z# Z b( `) `# H! i1 X
int device_add(struct device *dev)
. ?& E6 \8 R' ^/ r+ Q& S{0 s0 \0 ~1 W1 s/ w- K/ I! y* b) ]
struct device *parent = NULL;
5 Q$ z! n# H+ w) y& _$ }, i% p struct class_inteRFace *class_intf;
9 l( R, |$ J4 G int error = -EINVAL;
# R6 m0 `, S5 B) f3 X" |
2 \. _& [, f( _0 y8 }& R dev = get_device(dev); /*增加引用计数*/: N. M8 _& z" K% b+ J1 o. X7 R2 k* n
if (!dev)
- T( y. ]( H# K% y' B7 m goto done;# d) E% r3 K# W8 o& F
( {: | o Q+ K$ j! u. q dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); /*分配device_private结构*/
# R4 d, s$ M; M7 N, s, N if (!dev->p) {* K! h9 |4 {* _" T; [3 M9 {" D: U: S
error = -ENOMEM;
( ^& b0 A& L% Q- ^ goto done;
_# C; |# }7 V5 F7 C! e; Q. y }5 T0 g" [, T+ K! g2 U, u- x
dev->p->device = dev; /*保存dev*/$ \$ l$ J X* o0 v
klist_init(&dev->p->klist_children, klist_children_get, /*初始化内核链表*/
/ E' j, p* Q0 k5 Y7 p klist_children_put);* C3 L; R: H9 ~7 u7 D
) v: a q7 i! U /*: I# D; k2 r0 u& }& z, E
* for statically allocated devices, which should all be converted
) ^& D5 ?" E6 u$ b) s4 \ * some day, we need to initialize the name. We prevent reading back, s" d1 F6 R5 N4 n& A, M1 I: M
* the name, and force the use of dev_name()
. `5 m; O3 e. v) x, D" }8 V5 G9 m [ */
! E3 a: X8 [3 ]* m; x if (dev->init_name) {0 G" b5 S" e R5 V" V9 F
dev_set_name(dev, dev->init_name); /*dev->kobject->name = dev->init_name*/
& |; H( A/ V: V' T& g$ f' X dev->init_name = NULL;! d. k4 X& a) k) i* X3 w
}
; d2 E2 q. u2 [4 Q# }+ r; q/ u9 _& b3 b
if (!dev_name(dev)) /*检查dev->kobject->name*/
% c8 ^+ A# v- i1 X) Y$ x; L goto name_error;$ K# L/ y" M1 f( |% M6 q" l- d8 @
4 w3 ]8 A3 {$ { pr_debug("device: '%s': %s\n", dev_name(dev), __func__);4 O% \! g4 Y+ x7 |6 J6 V5 _6 y
: D o+ v4 f. n6 O6 k" q" b3 } parent = get_device(dev->parent); /*增加父设备引用计数*/
; S$ r8 g1 w% v$ p8 P setup_parent(dev, parent); /*设置dev->kobject->parent*/# A$ E! E W# J
y3 f3 e* g" w/ D! |& A /* use parent numa_node */
( {* Z1 G. ?+ U. W! l8 D2 O if (parent)
$ k0 ^" y) e2 ~1 |! q. A set_dev_node(dev, dev_to_node(parent));
+ v+ r) e8 d7 y4 s8 P Q: J* ?1 q0 M, V. z, U% l3 M' f& _
/* first, register with generic layer. */. N$ _: P- g: Y3 o$ y' K
/* we require the name to be set before, and pass NULL */
- g( A6 J$ Y p7 Z7 Y1 v /* 执行完以后,将在/sys/devices/下建立目录XXX,目录名XXX为dev->kobj->name*/& `% ^9 q) G9 V# m B! o! i
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); 3 e7 Y6 u) W9 K( j, v) u9 x
if (error). m% q/ E5 D" g" f% h
goto Error;
7 U& v& P! {6 q U* z( U" v5 X) C; T! T6 T; N4 V; D% i. q4 p
/* notify platform of device entry */' T7 _, ~' k7 m- \
if (platform_notify). O1 f: g r9 E& v/ V6 Z" v
platform_notify(dev);
7 o: N; V/ O$ ]: A$ V/ V$ b5 I& v, O; c; j
/*在XXX下建立文件uevent*/
# u3 q, n: P' o0 O0 Z! t' M2 Y error = device_create_file(dev, &uevent_attr);
9 h' W: I" E# V/ t if (error)1 h5 v! P8 N6 S; _. z7 M# m( S4 B
goto attrError;7 x4 o. ^9 S1 B3 Y6 O' Y
1 s4 f4 V8 J4 q! ^" |
if (MAJOR(dev->devt)) {/*主设备号不为0*/
4 z: J" |6 Q1 J, H error = device_create_file(dev, &devt_attr);/*创建属性文件dev*/
+ x5 t* M4 ] [# W7 f if (error)3 H; K9 ]! \6 O% s1 b5 t! U
goto ueventattrError;) n0 E& R' a6 C2 D% m7 y# ^! v% E
) Y; x/ T% ]4 ~
/* 在sys/dev/char/下建立symlink,名字为主设备号:次设备号,该链接指向XXX */
1 m: p* t q* Z- @: |' w7 v1 F error = device_create_sys_dev_entry(dev); 6 M2 K, B+ H7 v. j; C* Y2 ~
if (error)
* i, k w9 G- E; C goto devtattrError;
/ d- C7 j0 ~: g9 @1 Y' E* r, v+ f! ^ }) P) \ T3 h$ U6 n% I
8 Q6 `% c. e( B& D' ~ error = device_add_class_symlinks(dev);
0 [( s, w! ~1 [# |0 S if (error)4 j8 |0 `0 c p0 K, t
goto SymlinkError;
9 m# A& H! S: { error = device_add_attrs(dev); /*添加类设备属型文件和属性组*/
# a% s$ h! O& _ B- \ if (error)) X- @8 y& D6 p* z. |; j
goto AttrsError;
) M' I! `/ B/ d) o% K( B6 I b error = bus_add_device(dev); /*添加3个symlink*/& t3 C. Q$ N" g% M
if (error)
+ ~' p u6 q: B' G$ B# y goto BusError;* ^+ z* K A3 T# X) b7 s
error = dpm_sysfs_add(dev); /*创建power子目录,并在其下添加电源管理的属性组文件*/* J. ?) ^0 D2 w3 Q% L4 ~
if (error)$ s+ t( _- t8 B. V* n# J/ K
goto DPMError;
: y4 C3 ?8 x# s9 B; L+ a6 f# v3 x( g device_pm_add(dev); /*将该device添加到电源管理链表中*/% o* ?$ h/ A2 {0 b3 k0 C0 ~
0 |, h1 U/ i0 B* R' c /* Notify clients of device addition. This call must come
& }# L8 {- G0 U8 q# i1 {" q' u * after dpm_sysf_add() and before kobject_uevent().4 H9 e: `6 K+ `9 x; l
*/
& O- S# H, E( [1 b' ^ if (dev->bus). y, ]$ b# h1 H; a" @
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- u4 }5 S( c3 c- ]. O BUS_NOTIFY_ADD_DEVICE, dev);( p! M8 ~" r% ^3 z" {- ^8 t+ H
) Q0 B7 Z) ^& G. @+ ~* b: t5 `
kobject_uevent(&dev->kobj, KOBJ_ADD); /*通知用户层*/& ~ s4 ^4 _3 {; ?% W
bus_attach_device(dev); /*将设备添加到总线的设备链表中,并尝试获取驱动*/7 A& `1 Q/ v# |8 S
if (parent)8 t' j2 v$ M9 N& x' {1 ^
klist_add_tail(&dev->p->knode_parent, /*有父设备,则将该设备添加到父设备的儿子链表中*/
& T4 X7 a0 ?; S V &parent->p->klist_children);9 U/ z6 _1 s& E3 h ~( p
$ c8 B% u H( T( q8 ]4 ?% | ] if (dev->class) { /*该设备属于某个设备类*/
. J# n( M0 V( v4 g2 ]5 h+ y mutex_lock(&dev->class->p->class_mutex);3 {5 [9 n. E8 O9 _- H
/* tie the class to the device */
! W+ S5 [- z. c) a O klist_add_tail(&dev->knode_class, /*将device添加到class的类设备链表中*/
0 V% Q0 G8 D' z4 u' i) Y) \ &dev->class->p->class_devices);# z4 {. G) g8 N! Z, N* {
2 Q( t# Y& V' k5 b7 c
/* notify any interfaces that the device is here */
; s4 K% ^2 z4 g2 f; Z: P9 ` list_for_each_entry(class_intf,
* c8 c; {* h0 B &dev->class->p->class_interfaces, node)+ i! _) i' Q. G0 c2 ~: F
if (class_intf->add_dev)
) q* r! _; f( g. Z4 C. q! ?7 Y class_intf->add_dev(dev, class_intf);3 d, k5 v- b' S2 s+ R, N
mutex_unlock(&dev->class->p->class_mutex);
$ ]% c5 _6 |- Q4 x9 W1 e }. k/ V& N5 g( |
done:
`* i! d4 o. h) e$ W put_device(dev);
9 z3 T3 a. E' a V" W! S$ u1 M return error;
5 v# ?; _1 k: g/ u( {( y DPMError:
0 F: h7 a6 y5 \5 t# _' z& s bus_remove_device(dev);! v) E) Q( V& X
BusError: ^0 c! H# ~, p) T) B1 F1 t3 ?" N
device_remove_attrs(dev);) b( r8 A4 N4 `6 C
AttrsError:- u; B. H* |9 Q: C; w( V- m9 y
device_remove_class_symlinks(dev);
5 n: d# y: c& Y. { SymlinkError:
0 m" e5 N) L$ I2 ]$ O- p; C if (MAJOR(dev->devt))0 s, Q7 }2 @# C. Z( D
device_remove_sys_dev_entry(dev);4 Z/ U" H* U/ m) r& B" b+ E; h- h
devtattrError:
! o1 o( U9 `* N1 l) Z if (MAJOR(dev->devt))
1 z! M4 H, N( g device_remove_file(dev, &devt_attr);
9 U" j, r4 ]" ?9 F5 h. y1 s- v ueventattrError:" Q3 I# P4 j3 h5 | }, B4 ~! Z
device_remove_file(dev, &uevent_attr);' o# {% C6 |1 v
attrError:/ f2 l! V, D( ^8 e/ Y+ U8 i4 l
kobject_uevent(&dev->kobj, KOBJ_REMOVE);8 R- e* Z6 `6 }, K* t" f0 D- W
kobject_del(&dev->kobj); D7 `% |2 {- j# ?& w6 g* n0 u% _
Error:4 Y' r6 i! F- z) k& `
cleanup_device_parent(dev);
0 I' f$ X- s1 ^5 z# I. Q3 c) g if (parent)
4 {- A1 j* `4 I; i put_device(parent);
6 ]( l5 ~1 G5 x" X4 @% p; `name_error:
5 _: o" G5 }. n7 `5 {) H S kfree(dev->p);
" w% Y$ y a* v. b/ d r dev->p = NULL;
" h6 B& {/ i0 a8 ]/ A' ] goto done;
& n# c; l0 A+ U( Y}
' e& Y. g( l d/ p/ N7 f! x, b2 _4 K& G R
该函数调用了非常多的其他函数,接下来对主要的函数做出分析。& q! {& c! D2 d7 w
6.2.1 setup_parent函数
6 `2 P9 a# s: j9 d+ ^下列代码位于drivers/base/core.c。1 z! Z4 ?' k# r& l6 H
static void setup_parent(struct device *dev, struct device *parent)7 X z e$ w, j6 J W/ S" @, s
{/ d U9 ], w$ S3 f& k# O
struct kobject *kobj;
0 v0 t" k- A$ V( S* K9 B0 ]) ?- i kobj = get_device_parent(dev, parent);
% ^0 i" A4 \# H4 O, o' }* f. U if (kobj)0 A ~9 T- @0 o1 n
dev->kobj.parent = kobj;
! |4 R) l1 G6 n}
# x, t, C: ]% b; H0 |5 Y9 @. o
4 C4 `* k+ g$ g4 N1 dstatic struct kobject *get_device_parent(struct device *dev,3 U5 u7 G7 I; i( b
struct device *parent)
( k, h1 }3 Y' k+ B5 m{+ i' A" h i% V# J/ n
/* class devices without a parent live in /sys/class/<classname>/ */! [9 w' i$ u0 T' v" Q
if (dev->class && (!parent || parent->class != dev->class))" N( F! u2 o( V6 x
return &dev->class->p->class_subsys.kobj;
: K$ Y0 d6 | p4 [* C; F; U /* all other devices keep their parent */ o" J% ~' o. J( c8 H$ K1 ~) G* w
else if (parent)
, `* Q+ f5 q _" y v Q return &parent->kobj;
( y" M0 K; b3 ]$ Z7 _' A; K5 e, }$ V! j
2 L6 N( E3 B$ F) S return NULL;; @5 v5 {8 b4 q k9 c. e$ _
}
* I+ P- h/ `9 K1 d% f6 @该函数将设置dev对象的parent。在这里实际传入的parent为NULL,同时dev->class也没有定义过。因此这个函数什么都没有做。
& V6 W2 w* u' ~6.2.2 kobject_add函数
6 l2 X* i M; o5 _1 I3 t下列代码位于lib/kobject.c。
# J' {8 F$ E7 O7 _7 @/**
' ?/ ^3 R$ y1 \0 G, ]7 y! b0 K J; a * kobject_add - the main kobject add function* e* r& k- S. q, t# X s" ^ R
* @kobj: the kobject to add$ C5 _8 y( h6 R3 e* C7 ~9 Y: ^
* @parent: pointer to the parent of the kobject.
6 N' J" @' P3 E) U" p * @fmt: format to name the kobject with.5 O7 S* M5 z: r# x X
*
1 H6 Y7 m& x7 c0 Q1 ` * The kobject name is set and added to the kobject hierarchy in this6 T& ]) |8 [! [; W
* function.
/ r) W+ F. _ _% H# u7 v6 l, r *
g. i% m: M% H4 n, |0 g * If @parent is set, then the parent of the @kobj will be set to it.
. M3 K% v! e4 I: M; ^4 t * If @parent is NULL, then the parent of the @kobj will be set to the
* V! k9 K. l8 }2 Z( m * kobject associted with the kset assigned to this kobject. If no kset
" p3 r3 k# t8 Z9 O) I7 B3 d * is assigned to the kobject, then the kobject will be located in the) {4 V1 p0 J; y8 p* \8 W
* root of the sysfs tree.
# O2 _6 Q0 B* [" c3 {! r *1 R# V$ E. y( }* K/ o6 A3 @ F
* If this function returns an error, kobject_put() must be called to, j7 f6 ?0 d1 Q& e
* properly clean up the memory associated with the object.
& ~0 V; W/ U1 n* l/ j * Under no instance should the kobject that is passed to this function. N8 K' ^8 C, S2 ^" q$ `
* be directly freed with a call to kfree(), that can leak memory.
/ f* I* p' h* s5 g *: C' J6 o. w6 H' _ M
* Note, no "add" uevent will be created with this call, the caller should set
, k/ @3 j. X$ J+ `5 R: J * up all of the necessary sysfs files for the object and then call
% ~6 u# d3 c, z. m! s * kobject_uevent() with the UEVENT_ADD parameter to ensure that
# l5 o8 A* E: B v * userspace is properly notified of this kobject's creation.2 X2 a; j4 i* e5 s# @8 P5 K
*/
8 O, E" Y/ _( R3 f5 Kint kobject_add(struct kobject *kobj, struct kobject *parent,
- |" r. \# v5 s z const char *fmt, ...): ^- Q; l. s6 o( X( g. M8 l) F
{: j, \9 ~8 q* p
va_list args;' Q. Y" {! j; [, y. q- ?/ U' \
int retval;
* {- K( O8 W% \, i6 N3 D8 s# @: h# S% x, d& ~) r2 a5 V& P
if (!kobj)
( @0 w# I8 e- f3 {! c return -EINVAL;! _2 H' P/ w# x0 U7 g3 E8 H7 S
! a H! B. s0 m2 I+ L; a- V if (!kobj->state_initialized) {
9 L- G. ~! u( @0 W5 R. L0 i# o4 {$ ? printk(KERN_ERR "kobject '%s' (%p): tried to add an "
! m3 @$ {1 j8 y8 v "uninitialized object, something is seriously wrong.\n",
. s" U$ e; o( i Z. [, d e3 p kobject_name(kobj), kobj);
3 P. A, E* t2 |% e; [3 h. t dump_stack();2 i5 {9 a5 X J. u. W2 O. i, y( K
return -EINVAL;
5 F5 ~0 J2 x' E( P/ d& N* }5 n }) b; o# X, K$ m0 _
va_start(args, fmt);) R! x" k( {3 U) F J2 \9 z3 O) A
retval = kobject_add_varg(kobj, parent, fmt, args);
~0 w& d* X* x+ j" m; I( ^ va_end(args);1 }0 r# Z, p! }: g y
' [* J. D9 U7 e4 I; H( g6 Q8 M
return retval;
b( S' X4 a9 C: }}3 ~, `3 M1 |( M6 G g4 `
EXPORT_SYMBOL(kobject_add);
% H7 s5 g! g7 B9 d* m
2 W& b$ z/ s4 qstatic int kobject_add_varg(struct kobject *kobj, struct kobject *parent,- O/ a. b# n2 m3 F# {
const char *fmt, va_list vargs): ]& m9 _! B1 S
{# m+ j9 x; n, Z" F1 m
int retval;
6 f$ s7 ~6 y; q& B) p9 k1 L8 F2 z" l6 _6 a9 `% h! ]
retval = kobject_set_name_vargs(kobj, fmt, vargs);
1 f% U2 c0 H' T7 E& m \ if (retval) {/ u+ d% s( B. M
printk(KERN_ERR "kobject: can not set name properly!\n");
1 J0 C6 s8 U# _) K/ U+ D return retval;$ s( D' r2 |: }) t2 S
}
. d) |' E( e5 }7 M, i2 _ [8 W+ n- I6 e4 l kobj->parent = parent;
5 z |+ A9 u& j/ q( M0 b, P$ w return kobject_add_internal(kobj);
+ k w% P" v' r( m4 W}6 Q8 ]4 a1 G; W$ F1 l
3 e8 W- m V4 c. E- G
static int kobject_add_internal(struct kobject *kobj)
* ~1 z( ^6 ]7 A" J{5 P' Q4 m. ?; M: g5 h
int error = 0;, f' N* r0 R! j+ G+ W
struct kobject *parent;- M2 Y" `9 ~3 p' o1 r* c ~
; }/ X |, c4 n* z( C" Q2 k6 \
if (!kobj)$ F: X# @' @0 H4 l
return -ENOENT;
* `; Z7 F. Y; L. V* [; I* d" | /*检查name字段是否存在*/ m8 |5 G8 B7 @3 G+ s3 {1 j1 ~
if (!kobj->name || !kobj->name[0]) {
* j) c9 Y& y0 a) K WARN(1, "kobject: (%p): attempted to be registered with empty "! m6 q" F0 } t, f: F# O& s
"name!\n", kobj);/ ~( a7 _* t' E1 r6 [
return -EINVAL;4 M/ H8 W8 u" g% \+ j7 N
}
( E0 ~) ]+ t+ k9 h& m f* f9 R5 n( e( Z
parent = kobject_get(kobj->parent); /*有父对象则增加父对象引用计数*/
& w7 {: A0 [2 F6 P5 a7 q2 L" ^0 ]/ d: J/ k, D* W
/* join kset if set, use it as parent if we do not already have one */6 g, e" X4 a, n9 T& b0 R8 C
if (kobj->kset) {
' ^, N4 P; s) n2 K) b$ j1 h if (!parent)
. P* @4 E' Q& p$ q' N1 ? /*kobj属于某个kset,但是该kobj没有父对象,则以kset的kobj作为父对象*/
9 i, K7 N" Y% [, i# i7 Z9 o parent = kobject_get(&kobj->kset->kobj);; L# I x, U$ I, s; Q
kobj_kset_join(kobj); /*将kojbect添加到kset结构中的链表当中*/7 f1 `+ Q2 C, c+ {, v4 c
kobj->parent = parent;! d+ B, O2 T$ L; h R, n' A, V
}7 _6 ~- N4 W8 ^2 ]9 i
' y% B4 h8 Z6 ?1 _% T4 a( `
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
3 F. L% E# K7 l3 {7 m+ D kobject_name(kobj), kobj, __func__,
4 S9 J/ Q4 {$ c/ h9 O parent ? kobject_name(parent) : "<NULL>",# @+ d* X+ o- q5 I0 {6 E7 V: r
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");* J1 T" I( K% I: Z3 S4 ? z
f0 S* B% v7 o4 n6 a7 _
error = create_dir(kobj); /*根据kobj->name在sys中建立目录*/
1 ~! e/ j. X. @8 d$ l0 Q if (error) {8 ?: g0 ^% o% h5 _7 P- G, J
kobj_kset_leave(kobj); /*删除链表项*/
/ w# J* G6 v/ U- X kobject_put(parent); /*减少引用计数*/
4 H, L6 {2 h1 ?$ X kobj->parent = NULL;# n/ {2 G( f" t- D7 k. X& h2 |
% j9 o6 m. U# u9 a /* be noisy on error issues */
) Z1 n# l+ J0 Z. P7 z if (error == -EEXIST)0 t! B5 e! n2 a) N
printk(KERN_ERR "%s failed for %s with ": p- P& f- p' z. D$ M
"-EEXIST, don't try to register things with "% i7 R9 [( O, i6 }6 Y: _
"the same name in the same directory.\n",, b0 w; q' l$ D/ ^
__func__, kobject_name(kobj));. V' T/ l; I1 b" W
else
0 f) l7 J/ i$ H8 ] printk(KERN_ERR "%s failed for %s (%d)\n",; e" A* A4 \3 w8 H
__func__, kobject_name(kobj), error);
! b9 D. H( b! p9 Q+ q5 g; n dump_stack();, T2 T; x- L4 Z4 ]/ r7 E) J- w4 ?
} else
" P$ V- N t0 g kobj->state_in_sysfs = 1;3 ?! I6 b; b$ Q, E
8 o3 U" @) Q" D' K8 [) X; Q; r
return error;5 \7 B; Q$ a9 O4 ?
}
: F% p) {+ ?1 g3 C在调用时,参数parent为NULL,且dev->kobj.kset在6.1节device_initialize函数中设置为devices_kset。
/ W( _3 V- U2 p7 H$ j8 D: h而devices_kset对应着/sys/devices目录,因此该函数调用完成后将在/sys/devices目录下生成目录platform。% r; t0 i6 P0 o2 ?) |
9 N. x( ~/ N$ q. g* i
但是这里比较奇怪的是,为什么platform目录没有对应的kset对象???' \8 `4 v# ?4 x! R7 |2 f* B2 @
$ b- h" a* M) @% L6.2.3 device_create_sys_dev_entry函数9 d1 a. t* m- h: ~+ `) V6 ~2 f
在调用该函数之前,会在/sys/devices/platform/下生成属性文件。接着如果该device的设备号不为0,则创建属性文件dev,并调用本函数。: G$ X' Q& w d, y4 ~; @- J
但是,在本例中设备号devt从未设置过,显然为0,那么本函数实际并未执行。
/ c7 I) V7 c' ~; M6 m
0 n# y6 p5 Y- n( z# f下列代码位于drivers/base/core.c。
) W8 U3 _/ o$ V# z, ^1 ?! Sstatic int device_create_sys_dev_entry(struct device *dev)
0 E) W: \& |8 p: @{& h0 u g1 y2 v* v6 V; m
struct kobject *kobj = device_to_dev_kobj(dev);
5 Z0 i( A7 t# f$ i9 [0 R, W" C int error = 0;
# [6 @$ w8 \( W3 k1 `. l8 J char devt_str[15];
* l9 P+ i# J8 x
% R" {1 @7 B4 p9 [, K if (kobj) {
$ ^- T' W, X, Z- n9 h% m, n7 b1 ] format_dev_t(devt_str, dev->devt);
6 x: ~- X& }4 ^3 z W/ o error = sysfs_create_link(kobj, &dev->kobj, devt_str);7 V; ^( d/ q/ Y! w% \% V) c0 H. B
}
# p2 @$ M+ @ l, S( O5 W) _
9 M5 N7 u* [+ a return error; ?; q% |% b; L+ s/ X0 L! Y
}
+ ^3 b( [5 Q3 u& B, J( l/**
6 g. y8 M9 G6 `) e3 z * device_to_dev_kobj - select a /sys/dev/ directory for the device8 d$ ~0 q$ }) \0 t
* @dev: device
* [9 M# m/ A) U1 Q+ x$ v% Q *1 k* E% j6 @: D
* By default we select char/ for new entries. Setting class->dev_obj: H$ M& H. @% q0 C8 K" e
* to NULL prevents an entry from being created. class->dev_kobj must
0 Y' t8 n: R! j' }6 Z4 T- } * be set (or cleared) before any devices are registered to the class
5 E( u1 ^3 Z9 k" Z. q% Y9 G * otherwise device_create_sys_dev_entry() and4 z! J" P) e8 D; T4 w9 B; t
* device_remove_sys_dev_entry() will disagree about the the presence
+ t0 X a6 U" C9 D! a# r * of the link.3 `: d8 ~. h" m* G
*/* ], m8 O, p3 F/ r( U. l0 _
static struct kobject *device_to_dev_kobj(struct device *dev)) m$ @1 m4 w- s5 m* ?8 N
{
! Q0 @& Q1 J; E) \3 D struct kobject *kobj;
$ Y: h' }( u: n/ F5 n+ ?
\9 K0 r4 u) ?6 {% H& B) p4 q if (dev->class)& F5 \, i* ^, I8 {. _6 c- O
kobj = dev->class->dev_kobj;4 {+ q8 ?% V% G1 b
else
7 {1 L# Z" }: h4 m% S7 i kobj = sysfs_dev_char_kobj;7 M4 r) @& D: U6 G3 I4 a' ^
a$ M5 w* |/ z. e: o+ S% Z
return kobj;
+ Z% y4 b; i/ j# q+ H}
/ _4 m5 |0 A( ?+ m7 f
4 W. ~ Y3 r' y5 u. F: O/ ~# s6.2.4 device_add_class_symlinks函数
8 S5 Y/ j8 F6 e4 h. M由于dev->class为NULL,本函数其实没做任何工作。
; Y) c& l! F# I x( T: X% d8 d: q% [2 s& Y' K: q
下列代码位于drivers/base/core.c。4 G9 O/ W; k$ T; P
static int device_add_class_symlinks(struct device *dev)6 ?' W6 ?8 Z$ h ^- d; U
{% D. t) F" o7 o* v
int error;, f! |/ {5 N* ?1 G' z' ^
8 i( q1 C. n0 @1 B# ?& k
if (!dev->class)
+ n x5 W% D7 S' l return 0;2 v3 C$ ~$ i, K' x, T5 S7 s1 p9 K
: D P7 x6 v& _7 \5 g( O
error = sysfs_create_link(&dev->kobj,4 o4 |! W! x2 _9 n4 o* H* V1 ~
&dev->class->p->class_subsys.kobj,! _& P9 e# ~; x, H+ m
"subsystem");
% ?# k' f3 o0 ]- j! m, g, A if (error)/ g+ q$ o$ ^/ D$ i4 h5 l
goto out;
* Z5 W& _; a; O
1 |" [0 i& Z8 h8 ^, G4 V8 p2 e3 l#ifdef CONFIG_SYSFS_DEPRECATED
, [3 F( h* [' q% P! D9 t0 a% r /* stacked class devices need a symlink in the class directory */3 j, a9 `! s4 d2 F4 r3 J
if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&. H% a# }1 h9 s* {6 {/ I2 ^
device_is_not_partition(dev)) {& q# o, f2 e M* V: [
error = sysfs_create_link(&dev->class->p->class_subsys.kobj,2 p3 u+ ]: T, N3 m+ |( l6 v
&dev->kobj, dev_name(dev));& R2 w. G! D* y& `- u" {
if (error)
/ L; { V1 V# M( { goto out_subsys;5 {5 y1 \! u+ ]: `
}) [4 v- [2 E+ g8 g3 n* U
/ }) d$ o( _3 H( ~/ ~# T if (dev->parent && device_is_not_partition(dev)) {$ ^1 t" r' c f) r, Y' ^" m
struct device *parent = dev->parent;7 |9 A1 l2 ?6 p
char *class_name;% ?: g4 y* x. C$ A' r: q; `( @
/ A% l: W3 j( t: E
/*. t/ w h! @+ t) d. R9 V: n, H/ T, o
* stacked class devices have the 'device' link. @+ h$ b3 L) i* m, y/ U
* pointing to the bus device instead of the parent
) w) S/ p: e5 f% a3 F/ w- b */
% ~& e: ]( u, C2 W6 b. g! y while (parent->class && !parent->bus && parent->parent)
+ Q; ?' y u6 @* [# m' x parent = parent->parent;1 v' g$ O1 y1 C" H% _, O; q
I$ r3 H3 O" @; _
error = sysfs_create_link(&dev->kobj,
; p; m; s& ~' @# b' l% k5 ` &parent->kobj,
1 \2 G( ?4 X1 c& J: f/ f2 ` "device");
4 T+ X+ z$ l* ]2 ?* N- z if (error)7 l7 [; W# U. X2 w; f# X8 ]& l: \1 o9 {
goto out_busid;7 ^/ M9 C& C T/ T
4 x. l; T( O8 K) S8 ? class_name = make_class_name(dev->class->name,
& c& p( i( t- _2 @* i &dev->kobj);6 N2 u% N, Q/ a! I1 d, z2 B0 i
if (class_name)$ U2 k- O6 k7 w
error = sysfs_create_link(&dev->parent->kobj,# @( D+ K* W+ H
&dev->kobj, class_name);4 ?' R6 O4 x% e9 L7 P8 S) Z, G2 Q& F
kfree(class_name);& d$ E( n( Z# f) Q
if (error)
& k" K" |: ?" C goto out_device;
$ i8 q* {/ f9 M$ @! P/ s' f' y( z }
3 s8 D" V5 _$ I$ ? return 0;/ I4 F6 i! n+ X% n1 {5 W
2 c" k' h% U, C% P" p. i
out_device:
|9 [- C: R/ Z9 {, S/ Z if (dev->parent && device_is_not_partition(dev))0 v/ L; p% d8 {% x
sysfs_remove_link(&dev->kobj, "device");
. P! u$ I5 z" a4 wout_busid:
4 q# Q- i7 w$ N# G/ Q0 ? if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&- e' }/ z" L. x
device_is_not_partition(dev))
, _+ H, i* Y0 N sysfs_remove_link(&dev->class->p->class_subsys.kobj,& A5 u2 r- l+ t, G) a, J: r* ]
dev_name(dev));
, }- p7 l' S" q6 ]#else7 F3 v) z% Y0 W
/* link in the class directory pointing to the device */9 V7 o$ o. a0 }& h5 U! Y& Q
error = sysfs_create_link(&dev->class->p->class_subsys.kobj,7 C2 P3 c4 |, i+ D7 ]/ K
&dev->kobj, dev_name(dev));5 B) Z' ~" ^3 U H
if (error)* U% w$ P* A0 j/ V, r8 Z
goto out_subsys;
- Q: ?1 A1 m6 E9 P5 F3 x, }8 v* r+ J2 R9 n' H7 O
if (dev->parent && device_is_not_partition(dev)) {
. F6 }6 W/ ~2 b; \! c5 L error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,, \3 f/ B4 Q3 C5 p) |
"device");
+ ~$ F) U3 y* ` if (error)
/ K! ?' ?: m4 z* w* J, F, A goto out_busid;* A# G7 b& i3 a/ p/ O* k0 ?* R
}
1 M* F$ c6 g- I. }3 o0 l return 0;
! l* C3 Z% ]$ S0 T
& ?. X) l. {, H0 J" t2 d4 r, ?out_busid:
5 m9 x; X% M, t sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev));
1 s3 ^0 L$ K" f C4 P#endif) e1 y: O. ?/ r5 h
3 I, s4 D1 b0 Q( W& [( f
out_subsys:; ?8 ~/ M2 `) e; j! w$ Q% V
sysfs_remove_link(&dev->kobj, "subsystem");
+ r$ u0 z+ h: ]& Lout:
# V+ _& v: m* |3 d return error;7 t; w. S) O8 g5 R. c) C8 n
}
' p9 C' N9 Z, e$ l$ G- H' C6.2.5 device_add_attrs函数# H! G' [' w: } W1 j
同样dev->class为空,什么都没干。
8 L9 w+ V5 t; W$ I下列代码位于drivers/base/core.c。
- B, m; \0 T( C$ q; Sstatic int device_add_attrs(struct device *dev)9 I# s$ G% o' L4 R
{ ~- ]1 o7 _ T/ \+ V7 e
struct class *class = dev->class;
1 L0 i: h7 K! O8 r) R/ f" H struct device_type *type = dev->type;5 l" X) H4 e) P u: _9 \
int error;
$ I% C6 h2 K( W! @0 M7 Q& w2 A+ w5 w2 ^9 u6 }9 U
if (class) { a, B% B! W* M
error = device_add_attributes(dev, class->dev_attrs);
: S7 c2 @1 t& k; ?7 b7 L6 j1 ` if (error)
3 j8 r* a. G4 A& q return error;* D; e& D p1 i
}
' N% C S9 f7 a0 ]( N0 G$ Q/ p: y, [% _2 K; T' d
if (type) {) |( L: x' y9 G9 `
error = device_add_groups(dev, type->groups);
6 N/ ^' C% v" k: c2 H9 I; `' v9 o if (error), l6 N6 Q" \1 e: `! L7 M4 y
goto err_remove_class_attrs;& r1 B$ t, L7 m, H- \. n8 B/ a
}
6 r D8 z3 ]$ G* V4 ^5 d1 W% o
* M5 `5 u' Y" T error = device_add_groups(dev, dev->groups);
0 D; f! H: o7 i8 R. m if (error)
9 i* y# c- }6 ]; i goto err_remove_type_groups;
, _5 N5 P' G* R6 g% C( `+ A9 D) X$ M5 l. |& Y* E6 ?
return 0;
) t9 ~ H* T% j7 g* p$ q, h8 X& I- D4 X% I T% n, o8 b! B8 L
err_remove_type_groups:
( j# [! P, G- r+ T% @9 M" D5 i if (type)2 R" J8 U$ s2 W
device_remove_groups(dev, type->groups);5 f5 F% H' A( w
err_remove_class_attrs:
- l2 h( R/ b6 [. W if (class)
( [7 A: n3 K/ |, o3 s. { device_remove_attributes(dev, class->dev_attrs);
; U: r: s7 m9 l- u) s/ g- c) `- r3 V7 {$ G
return error;
; b: T! |- G; v}
$ t% \% h8 R5 o# v& ]6.2.6 bus_add_device函数
. F$ O! _5 Y1 h由于dev->bus未指定,因此这个函数什么都没干。4 u7 G. f9 ?# b/ I
: Y0 K$ ?8 W6 Y9 x$ x
该函数将创建三个symlink,在sysfs中建立总线和设备间的关系。
8 E3 i$ c% o' n) T/ \% x5 y8 \: z9 c+ V5 p
下列代码位于drivers/base/bus.c。
$ e! x y. I) z. Q/**( B2 C |! F. S' P. t- _9 A
* bus_add_device - add device to bus
9 w. B2 `4 e+ X3 V * @dev: device being added
% g) i% b9 a' P- X *$ a$ C) Y( C/ P5 o5 \
* - Add the device to its bus's list of devices.% k" @ |- Z% L) z' o& ~1 [. o Z
* - Create link to device's bus.
( Y% g( h. T( G& s4 M */
8 o2 ]/ }) I9 @" ]! ^4 uint bus_add_device(struct device *dev)) R+ S D X2 N/ \; } o) E
{
7 [4 K' y% v7 t& J struct bus_type *bus = bus_get(dev->bus);! P4 r' i, U/ V) V- g( o0 v
int error = 0;4 Q- r. h$ Q* a `
2 m3 [8 X% M: Y. P# l if (bus) {& r* i, j0 R) Q( O/ A& S2 o1 y4 O; d
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));% X0 m8 }2 `* ?( @3 C4 j$ A
error = device_add_attrs(bus, dev);
7 P) `! d0 }) Z0 l' W ?. { if (error) U8 w& z% R/ m9 }0 K
goto out_put;
. _0 S7 a- |6 Z
$ h3 O3 j$ I9 m' S. X; V) R+ z! L /*在sys/bus/XXX/devices下建立symlink,名字为设备名,该链接指向/sys/devices/下的某个目录*/) M# \! m( b2 S3 Z
error = sysfs_create_link(&bus->p->devices_kset->kobj,3 I3 c n0 c: E" J W* H
&dev->kobj, dev_name(dev));
; r' q8 }4 t; G" R5 B if (error)1 Z7 c) U4 k) p$ d" o. w, J" F
goto out_id;
_2 O0 i& V" D: ~
$ o) s! t1 o9 y* x8 k /*在sys/devices/的某个目录下建立symlink,名字为subsystem,该链接指向/sys/bus/下的某个目录*/( t ]8 x$ J5 [) ]0 g' P
error = sysfs_create_link(&dev->kobj,
- y1 v+ Y' f, X: v y/ e3 E &dev->bus->p->subsys.kobj, "subsystem");
$ Q2 w% Y- w' }% Z" g if (error), n. ?% O8 F. ]- c. Y& p; [/ B
goto out_subsys;
9 S6 O6 S6 ~9 @) @" B* v 4 |' a8 E7 A9 F+ A$ I" h, V7 _0 J- f
/*在sys/devices/的某个目录下建立symlink,名字为bus,该链接指向/sys/bus/下的某个目录*/4 M+ l d/ k1 d7 j' |/ u' R, [
error = make_deprecated_bus_links(dev);& B. q4 z/ P& J0 l4 O% p
if (error)& F6 n$ J6 `( T% M4 B- L
goto out_deprecated;
6 N7 t, P/ v. e: F$ M }9 b+ l1 n' e+ D. G2 V S
return 0;
& y( V& ?5 x6 `
, S; l$ v: x7 N% j3 U K4 o7 Kout_deprecated:
% U# ^# `% q. Y6 Z, [. g sysfs_remove_link(&dev->kobj, "subsystem");
9 Y! U3 S5 E8 Y/ t9 C! sout_subsys:1 s$ T4 C( V" b1 R2 K; `! i
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
: l8 U f3 X- m& a C: vout_id:! H8 ^! ?- y$ D; x" L
device_remove_attrs(bus, dev);3 [6 l& |# H, a
out_put:5 C( l& C- x4 V/ Z& `! N
bus_put(dev->bus);, x$ J/ D9 O; T! w s- B; x
return error;, K4 o, ?8 _! E6 r8 O4 l
}
; S) b" x! Y) s/ H9 D7 X3 i+ w) F _' D
6.2.7 dpm_sysfs_add函数& S0 G3 E, T$ z
下列代码位于drivers/base/power/sysfs.c。
' l' x. j, A( G/ w3 W( ~: @, | \int dpm_sysfs_add(struct device * dev)
, g" P8 a! X+ P0 b{; s7 B$ {, X6 t& u( V4 ^5 D6 c
return sysfs_create_group(&dev->kobj, &pm_attr_group);
! d& S7 Q: `- }# N& h& b2 t}) y( ?0 @" |( i* |" I1 `* K* E
( O9 E% j$ A6 Q- T: Q# z
static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
" z8 O; u0 A. q- k( A: T$ ^' _5 w2 `( M3 m5 q
0 R( X- d/ ^- W; u3 h0 v9 b& x1 h2 zstatic struct attribute * power_attrs[] = {( k8 M* R: M$ N- J, j. O
&dev_attr_wakeup.attr,. S+ o$ p/ v% v1 x1 U$ @+ |
NULL,
! o8 f& j) F# y+ s& U e};- h: v% H. Y5 V2 M* |
static struct attribute_group pm_attr_group = {
' B. d0 Y4 X7 n6 X8 T .name = "power",
3 @# N& `* L1 c( Z9 i) w) D. z .attrs = power_attrs,
) P8 @- q. J4 q3 H4 {" U6 Q};: x7 G4 u ` D2 x" h
0 x- a7 y* c& Z* z% j该函数将在XXX目录下建立power子目录,并在该子目录下建立属性文件wakeup。* `7 j! T0 J# }, n; d
4 i: a, ]* N/ B+ M) B ~在本例中,将在/sys/bus/platform下建立子目录power并在子目录下建立wakeup文件。1 b( ~' N( X3 B1 A' A% u
6.2.8 device_pm_add函数
6 J6 L' h1 b. j) ^9 w. o下列代码位于drivers/base/power/main.c。
) y1 W8 R) G+ j- O3 R: @: x/**
( @; `5 H1 [0 d * device_pm_add - add a device to the list of active devices/ y. n! R7 F- r8 B- \9 _
* @dev: Device to be added to the list( a g J V0 B
*/ w: d4 \% g) Y" R; j' Z
void device_pm_add(struct device *dev)
3 s4 n L" ]$ T{* V' l3 N. ?3 f+ ~
pr_debug("PM: Adding info for %s:%s\n",7 \) Y* U% }0 z. v, \ f
dev->bus ? dev->bus->name : "No Bus",
3 K2 c3 Z. h' U6 M- T$ u kobject_name(&dev->kobj));
0 `! v8 K: e3 X' _0 B l mutex_lock(&dpm_list_mtx);( J x) M9 W& r4 n: P
if (dev->parent) {" X) T% s. Z. Q( B- C
if (dev->parent->power.status >= DPM_SUSPENDING); ^% e" z+ @) B0 a
dev_warn(dev, "parent %s should not be sleeping\n",
0 U; S6 N; x$ e) r& c# j% j dev_name(dev->parent));
$ n+ P ~7 V3 a } else if (transition_started) {- |" C1 I* [3 [ d1 y
/*3 |, r" T. O$ {
* We refuse to register parentless devices while a PM
( L* h* N: m/ O- O$ a" | * transition is in progress in order to avoid leaving them
! Z" H4 N/ k5 p, o: B2 W ~/ f+ w * unhandled down the road3 O) l M! e4 s& u7 w+ f" Q
*/
; g& O" I. I# _: ?/ Z dev_WARN(dev, "Parentless device registered during a PM transaction\n");' x: q% r& L% \0 P c- q7 e7 l
}
7 f2 ^3 {- {2 ~5 \9 f. U9 f9 f( L+ Z! `; B) ~
list_add_tail(&dev->power.entry, &dpm_list); /*将该设备添加到链表中*// B! B: _$ c& p0 `; { N
mutex_unlock(&dpm_list_mtx);
/ [/ W7 t) M6 T/ M& P/ l}
3 r: H' i2 ?* L7 G+ h; w) b# w6 x7 `) n2 c( s
该函数只是将设备添加到电源管理链表中。+ i4 ]; Z( Y$ K( y, l3 E
6.2.9 bus_attach_device函数
9 }; J6 |5 r+ |6 T1 m, `在本例中,由于bus未指定,该函数实际不做任何工作。0 O4 f6 E# h1 `; a3 p$ ~5 U
下列代码位于drivers/base/bus.c。1 O. C' \* F8 y( q
/ _! W& P( c" [/**
b# t' x, W7 ^) i3 U. g * bus_attach_device - add device to bus4 W) q8 b) a: L
* @dev: device tried to attach to a driver& f- ]& h0 f( F q, V8 p
*& p5 d' b( [5 \+ z' P
* - Add device to bus's list of devices.
& e) }+ H1 Z G0 u9 F& `: r3 }- [ * - Try to attach to driver.1 A* u, E7 I. p9 j
*/ I$ o- _5 i, y4 r& B
void bus_attach_device(struct device *dev): r( r! ?+ e2 r9 @
{& i* F& T8 H2 E; p: I
struct bus_type *bus = dev->bus;6 Q# u" J0 [2 H
int ret = 0;. ?2 [8 Y9 V5 E# ~2 Z
, F1 y# v6 l- {: D* @2 w) \5 o
if (bus) {
# X6 P4 G1 k- p9 } ~ if (bus->p->drivers_autoprobe)
, `$ L; I! n/ ^% g ret = device_attach(dev); /*尝试获取驱动*/
9 K5 p2 N: {- @7 Q; k& {: } WARN_ON(ret < 0);/ O6 |" I1 x& w) x) i7 l4 p9 D/ A1 N) X
if (ret >= 0) /*将设备挂在到总线中*/8 M9 l; i% V3 j$ u2 {3 C4 b
klist_add_tail(&dev->p->knode_bus,
+ O/ z3 _+ e) ~ &bus->p->klist_devices);
1 z/ J4 L3 C. h+ t* {1 T }
) t% [8 N8 ^$ [3 V6 `0 ~}; R% k# A+ ?5 z, T) u$ H
$ z+ S' L# b V/**
8 Q7 B6 F! [+ @& N: F: P9 C * device_attach - try to attach device to a driver.# p; m2 g/ ]9 O" J( T
* @dev: device. A; J7 i) w( q6 F$ \( f/ Y
*( E U7 v* @& o/ v5 c
* Walk the list of drivers that the bus has and call4 b7 V6 S! Y3 t# [, ^/ ?8 r
* driver_probe_device() for each pair. If a compatible& @. ~, d; i5 J, C
* pair is found, break out and return.
2 p; v5 m- {/ k) l6 j- V *1 I, P( j+ h9 o. s+ t) W
* Returns 1 if the device was bound to a driver;
5 u8 a- n) x) p$ w! Y * 0 if no matching device was found;
1 i, @: u6 ~$ l! @9 D$ u; N& w * -ENODEV if the device is not registered.
" j" Y. ]& O5 s! ~' V *9 h8 M' B7 v$ i1 I% w1 \6 e/ B( C9 V
* When called for a USB interface, @dev->parent->sem must be held.
9 d8 S- I7 a3 X3 D; V) m */
9 K7 d( i7 Q" n& `+ K! i* vint device_attach(struct device *dev)* [: w( _9 w# E2 `# S
{
1 Q! y4 F( o# y* e/ O int ret = 0;. T) [6 R. d J
3 _1 A' v6 z' h7 ?, I down(&dev->sem);2 |4 Y3 R0 O) Z y' u5 G% W
if (dev->driver) { /*如果已指定驱动,即已绑定*/
2 T; Z& A# I, j5 X0 y3 A) p r: o ret = device_bind_driver(dev); /*在sysfs中建立链接关系*/' z6 i) Z0 V: B2 ]" t
if (ret == 0)
8 }- ~$ \9 w) d+ C ret = 1;
4 e% C8 U7 O3 T1 y) } else {3 L% s# `6 u6 W4 p* H/ Q; A# M
dev->driver = NULL;& V7 S2 E5 R0 c; m3 Y. \' k
ret = 0;
& o; X6 R3 T3 o) { }
6 i, \- @& _& T. c% U } else { /*尚未绑定,尝试绑定,遍历该总线上的所有驱动*/- c8 j5 T. y- A- N0 c' z, x* m, c
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);/ `+ g$ t/ v, l( K
}. s) U8 t. W) a( o7 J9 b
up(&dev->sem);
6 m4 J& \1 W9 j! t( R: R! w9 d return ret;" h8 K' i4 e5 V% K
}- J' v/ f6 e) K4 P8 e' k" E
EXPORT_SYMBOL_GPL(device_attach);6 U, t$ C" a6 F0 K" \1 P
! D( }: Y8 i P: Z如果bus存在的话,将会调用device_attach函数进行绑定工作。该函数首先判断dev->driver,如果非0,表示该设备已经绑定了驱动,只要在sysfs中建立链接关系即可。
+ E5 m/ o e8 j
- p' p$ x$ d1 k: ^为0表示没有绑定,接着调用bus_for_each_drv,注意作为参数传入的__device_attach,这是个函数,后面会调用它。! }3 q) e3 y$ M' C2 w2 K4 u
" s$ q. q( c' X, b
我们来看下bus_for_each_drv:
8 N$ Z0 O, X$ l% j [ P5 J9 u' t3 u
/**/ T! Y* [+ Y' |( g2 Q0 G$ v
* bus_for_each_drv - driver iterator
: [6 |" V8 A2 b; Z& E. } * @bus: bus we're dealing with.
; ?; \. X" _; K! w/ l9 g * @start: driver to start iterating on.2 ~5 R. v+ n- W) E! C3 A
* @data: data to pass to the callback.7 m$ @! p3 h/ T/ B
* @fn: function to call for each driver.
+ \, B0 s L) g, V* y5 I *
3 p, |3 W8 w+ d: H7 S; a * This is nearly identical to the device iterator above.
1 q, U( b. \! O3 g1 A; r$ A * We iterate over each driver that belongs to @bus, and call3 @ ^ `4 f2 M& p
* @fn for each. If @fn returns anything but 0, we break out
( V! ? {5 u, X. F* p- V * and return it. If @start is not NULL, we use it as the head z, r0 Y# b& v5 J6 T, m
* of the list.9 l+ q, e. }0 l' D5 d" ^
*" _9 g6 v* F+ |
* NOTE: we don't return the driver that returns a non-zero
) W' U) b) V3 E4 [ * value, nor do we leave the reference count incremented for that
6 v" `! L4 m# x * driver. If the caller needs to know that info, it must set it- [! h U( R- D% b* e
* in the callback. It must also be sure to increment the refcount& x" J0 h6 K: e
* so it doesn't disappear before returning to the caller.' L- ~% b) ~9 a E5 [
*/
6 Y$ Y. W! z2 w. j4 j- x! b G; Rint bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
- k0 m) t- C- Z6 M+ F void *data, int (*fn)(struct device_driver *, void *))
& t9 q+ C/ S3 ?. ~/ `. j{) E. f) P; g( R
struct klist_iter i;! c' _) x; M- P# Q
struct device_driver *drv;# i" x w) u$ {0 V$ S/ V' r
int error = 0;
2 j8 a: J) P0 N4 X1 l2 T5 M6 i( y* p4 {7 L
if (!bus)! E3 X- G0 o% ]% y
return -EINVAL;) ~1 [7 i6 F+ L1 G1 R, x
4 a* Q- R3 ?. B# l4 J, Y$ ]0 d. I0 g+ G& l
klist_iter_init_node(&bus->p->klist_drivers, &i,4 C! w; K- C* r" i6 z. W+ ?4 A+ r
start ? &start->p->knode_bus : NULL);. u7 G, w1 ]2 y0 r" l9 J: k
while ((drv = next_driver(&i)) && !error)
* J9 P$ m% B7 u& A& W' X& M/ l1 o error = fn(drv, data);4 D0 r& i$ [( E* W2 V
klist_iter_exit(&i);
& S& L5 a, {3 U/ p return error;& P( j. c: K9 s3 B' @4 g* v& ?
}4 U: \9 Y$ M+ M7 W9 R6 z
EXPORT_SYMBOL_GPL(bus_for_each_drv);
" y. c$ { }) i0 J! C该函数将遍历总线的drivers目录下的所有驱动,也就是/sys/bus/XXX/drivers/下的目录,为该driver调用fn函数,也就是__device_attach。我们来看下:
0 W' g4 {: K/ p
4 K: \; c# S6 c" ~$ g! ^* qstatic int __device_attach(struct device_driver *drv, void *data)' z( W' G5 J: Z. V8 k8 K! }' R4 A* Q
{
# z/ ?- C$ w. C8 E. M) y I6 v struct device *dev = data;; O0 b% Y4 M- Q% A! X
, Z9 Z; t+ X; Y d3 r# I if (!driver_match_device(drv, dev)) /*进行匹配工作*/
0 ^2 ]. ]7 g# Q( L$ p8 u. t return 0;
% u8 M, b+ a, d
) g% A- M9 e+ _2 s& z0 D return driver_probe_device(drv, dev);1 }5 b/ m- d8 l9 R& t. w0 V' F3 j
}
. \ q' t$ }+ l; E: b1 g0 t. E
% M) P; k0 ]2 N C4 ^static inline int driver_match_device(struct device_driver *drv,
# ^8 R0 M# w+ t% K9 z struct device *dev)
" Q% d+ u# b0 k X X{
- F( {" {, g. Z* ] return drv->bus->match ? drv->bus->match(dev, drv) : 1;
) U+ l7 d, ^: p P p. z} 0 y% c3 Q8 R Q9 l
& s& t5 m. {/ g+ B, K' [' o: K' H. }
/**1 u" l m( q1 ]! c8 L. V) ]
* driver_probe_device - attempt to bind device & driver together
) w7 i$ g* H, L3 ^9 i, n" _& G * @drv: driver to bind a device to
7 r) }4 D0 L6 U, S5 F' s * @dev: device to try to bind to the driver
" x& Z* ^8 }3 X *: R6 u8 W4 W! V2 h7 P3 a
* This function returns -ENODEV if the device is not registered,
$ C* z% J( @* _$ o7 o * 1 if the device is bound sucessfully and 0 otherwise.; }8 X, z) J4 b
* [" K# Q/ L9 s3 e3 M% S2 x8 W- u4 ~/ a
* This function must be called with @dev->sem held. When called for a# @- o: ^/ P7 y. B5 E8 {' @
* USB interface, @dev->parent->sem must be held as well.5 V2 _8 d( x4 n6 _
*/1 k" u5 W+ G* ?& I! W" d8 F, e7 \6 @
int driver_probe_device(struct device_driver *drv, struct device *dev)
) t: O( T, Q! Y3 w8 o9 y{
- T' J' ^" \8 `* T$ t9 s& m, D# G* l& Z int ret = 0;
2 r9 u& a% g# i1 C* ^- t( r* ]$ C3 H( d& a" y- ^" k
if (!device_is_registered(dev)) /*该device是否已在sysfs中*/* u$ F: |3 i$ n- {# Q0 k
return -ENODEV;( h/ W, d" Z7 L1 r1 I$ V
0 P! X0 k$ l% o: w; [
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",* r" F4 {9 G/ o9 W5 w6 s2 {
drv->bus->name, __func__, dev_name(dev), drv->name);
% T5 f) g" J, l8 ]" B: I7 K6 y+ @" D* L6 B4 c
ret = really_probe(dev, drv);/*device已在sysfs,调用really_probe*/ & C/ H( p) I+ f: I
! i1 Q' L4 \( \1 B* G- F4 G
return ret;
& `9 T; @, k/ _0 c$ E0 D}
. } ^1 _0 p" d. U, c6 [& v# S8 @6 @' L# K4 k( Q S
该函数首先调用driver_match_device函数,后者将会调用总线的match方法,如果有的话,来进行匹配工作。如果没有该方法,则返回1,表示匹配成功。
% Z5 Z4 |9 @+ H: }, H4 V4 R0 |( q我们这里是针对platform总线,该总线的方法将在7.6.2节中看到。. j" z1 ^4 O1 u8 U
; \: v; C* @3 _. E$ C1 W
随后,又调用了driver_probe_device函数。该函数将首先判断该device是否已在sysfs中,如果在则调用really_probe,否则返回出错。% h0 E2 X% B7 ~* o( U4 ~, U/ @
0 W% d) G8 P( U) Z2 Freally_probe将会调用驱动的probe并完成绑定的工作。该函数将在7.6.2节中分析。 v( Y, ?# c( ^1 t6 h i
6.2.10 小结
: f% S7 v0 C9 X) ?9 W在本例中,当device_register调用完成以后,将在/sys/devices/下建立目录platform,并在platfrom下建立属性文件uevent和子目录power,最后在power子目录下建立wakeup属性文件。
8 V+ z; v" ]+ ~! c
+ i$ w0 S8 g( ?- B" O8 w1 b3 B6 _最后以函数调用过程的总结来结束第6.2小结。( l* E i' M' C. y
) o5 B$ V7 @* v W# w: J5 a
' l3 `0 i( ]2 {! Q
3 F1 I3 e; J- r9 z4 m- ?+ v+ U
* a5 t5 T7 _# ]2 p; ]$ f6.3 spi主控制器的平台设备
8 K3 H' M, R: {# [& z: Z. n! ?4 N) s本节对一个特定的platform设备进行讲解,那就是spi主控制器的平台设备。- U6 i, x8 D- n& G1 J/ s3 R
+ a1 ^4 e# d( W J. u0 w* w; `
在内核的启动阶段,platform设备将被注册进内核。我们来看下。& Z+ U' |9 N, r) h3 }, G0 Z
7 ^' T" i* z4 d
下列代码位于arch/arm/mach-s3c2440/mach-smdk2440.c2 n" z8 }- L9 B+ }; z
0 l- n8 S8 M: s4 v) C, M, w Z4 {
static struct resource s3c_spi0_resource[] = {
/ ]2 `0 o6 Y: s. Y3 i* @% ?, S [0] = {( Q2 `! i/ ~8 C% X
.start = S3C24XX_PA_SPI,
! L' k; g2 } l3 l .end = S3C24XX_PA_SPI + 0x1f,7 [$ V5 u5 x4 g3 U! o& x I! v
.flags = IORESOURCE_MEM,
7 i& ]& l/ X" K ]9 X; e },1 H# K+ i% e; }. S! x. D) A; n
[1] = {
9 J0 P2 K" T ~% `3 j6 a .start = IRQ_SPI0,
4 [' ]; m" m! {2 [% B .end = IRQ_SPI0,! l; Y6 o) \! Y4 o, n! f8 g
.flags = IORESOURCE_IRQ,
# J/ z$ [6 ? y9 Y @ }
* `& y, F/ ^( m0 o$ C2 J* {% K0 q0 o/ E6 n2 O/ q3 a
};
- K1 X) b8 h Q8 _2 Y0 F2 U8 q0 I# u( J+ z; J/ ~* q6 L' f
static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
, l0 g0 u' Z8 P. }9 w0 a
0 g6 F" O3 v* B# F2 _struct platform_device s3c_device_spi0 = {. {* K) p3 v* b% s9 u
.name = "s3c2410-spi",
7 l/ C/ |3 k/ _/ _: P .id = 0,
3 |' v/ Q, z, I .num_resources = ARRAY_SIZE(s3c_spi0_resource),- i& o% q1 b& g, B* F( G3 [
.resource = s3c_spi0_resource,+ x( I8 U _4 j( i$ o0 M+ q/ g
.dev = {& f$ E8 u# s3 P$ e) h
.dma_mask = &s3c_device_spi0_dmamask,
6 W: I6 m% J+ v# f1 u; o4 i .coherent_dma_mask = 0xffffffffUL
/ K# I) Q& S' w1 N( Q+ S" i }
1 J: C1 ^) w& y! B0 a: H. h};
/ I1 H5 y7 r8 ^( P- U2 E
- [0 d s: W5 ?+ S$ N' h. ?static struct platform_device *smdk2440_devices[] __initdata = {
3 o; D% L2 d+ Q b7 E &s3c_device_usb,
" x4 u1 B* {7 c$ M- Y: ` &s3c_device_lcd,
5 s3 E1 j! o3 D &s3c_device_wdt,
. |* Z1 }+ w6 L( b+ j# f# | &s3c_device_i2c0,
6 {0 V- _. d# y p6 k &s3c_device_iis,# f: U- b, P5 n9 \6 z
&s3c_device_spi0,
t, {9 r0 m$ o$ _0 c4 d};; P; W, B* O( ? v
5 ?6 c( J$ h( S8 n) z6 e3 f* C8 r7 v" ?) P
1 ^8 z7 T; e/ u0 b, ~$ |: h8 C; Kstatic void __init smdk2440_machine_init(void)" ? k3 F3 I3 [' P. _' f* w
{
* k* i7 f7 s& b7 P; S" f, O s3c24xx_fb_set_platdata(&smdk2440_fb_info);2 P, e# Q3 t+ `2 o% V* C
s3c_i2c0_set_platdata(NULL);
( t7 ?' j: }$ o) d
- j) P& r" l/ R6 Z. v: y5 d platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
6 C/ d I8 y9 @1 ]- ?3 ^: N0 T smdk_machine_init();
2 w) x' |) B. I6 q& Z6 r( D}; V9 S# V: z1 L5 e" R2 |- v
* k# Z4 p& |$ F1 R1 c
在smdk2440_machine_init函数中,通过调用platform_add_devices将设备注册到内核中。接着来看下该函数。5 l# s& {+ U# T7 g; m1 o- Z
6.3.1 platform_add_devices9 H/ B$ o2 X( U& j! w5 F. _0 z+ L
/**' G. p$ _: {; C8 E
* platform_add_devices - add a numbers of platform devices8 q9 k" g8 A$ F: m- o- V
* @devs: array of platform devices to add
+ i/ `2 B/ A# s2 {5 P5 M4 j* j * @num: number of platform devices in array0 D5 I+ Y0 Q3 w2 _
*/7 O6 O2 a1 X# a" d) K X5 ?3 m
int platform_add_devices(struct platform_device **devs, int num)* d8 o4 z/ b. U9 i* O/ [
{
& L/ L; D) N' Q& ^. r' Z int i, ret = 0;, j2 C& X k& s6 h
) N7 a B, j! J+ _; k
for (i = 0; i < num; i++) {, H) g. x" w# C0 {
ret = platform_device_register(devs);4 R' H& R- ~9 V& b' D& u' _
if (ret) {6 y, o# {* N2 F6 u
while (--i >= 0). P: _8 @) H0 c+ V1 C7 z/ y* E" h
platform_device_unregister(devs);; `7 u8 A4 a- Z' A3 A. A
break;5 R3 R: D) e! @9 K$ y; Y7 c* ~
}4 }: D6 _7 z/ H; v% n
}8 M8 E, P3 g, L- p% t3 e! p, M$ \7 u
/ J$ R: e! m6 p0 g" d G0 o
return ret;. ~( ?: U+ V+ [( \; a3 D
}6 H; Y4 C' v" ?" i
EXPORT_SYMBOL_GPL(platform_add_devices);
, S5 X& y$ N" b) {+ D% s3 j. x% T, C% y( h( Z. t. [4 ]! v! j: n
该函数将根据devs指针数组,调用platform_device_register将platform设备逐一注册进内核。
, N; l+ I. A4 g% u6.3.2 platform_device_register; {, T3 n; `" h4 l" ^
/**2 n- ]7 M. M) Q: p* B
* platform_device_register - add a platform-level device
! i. r! N/ H* ]) ^ * @pdev: platform device we're adding: ]1 @; ^6 v, t6 f3 R8 }
*/
+ }# m. \0 R F2 o* oint platform_device_register(struct platform_device *pdev) z! X0 h% w1 i& t0 E6 u
{; h, Y* D7 Y0 J' h5 x. S% j
device_initialize(&pdev->dev);
4 W. \+ t/ ^0 y2 V+ d7 f' p2 m4 l return platform_device_add(pdev);
2 B5 O3 j/ L# d9 X, o! o& B7 u}- i1 L. E1 r& R3 X% X" c
EXPORT_SYMBOL_GPL(platform_device_register);
, g/ p; y7 _6 C4 ^ a! r5 V% ?
k1 U( m0 ]: W8 V) x7 d7 M) Y1 B" }调用了两个函数,第一个函数在6.1节已经分析过。我们来看下第二个函数。1 B: o: `% c+ W# V, q8 L4 M. j
6.3.2 platform_device_register! c4 F3 g1 ~6 |4 {' q, N
/**' K. c) R, b) {; B5 I
* platform_device_add - add a platform device to device hierarchy% E; Z. B9 w/ B" E- x. L- E1 L
* @pdev: platform device we're adding
1 p3 T- ^& v- c- O1 o *
3 w1 [" y4 W& f! j7 ?1 Y * This is part 2 of platform_device_register(), though may be called4 b: x' R0 |, @2 l/ k% X
* separately _iff_ pdev was allocated by platform_device_alloc().
: v4 @6 c& m& w" b' w: ?) l4 q( U7 n1 q */
& b4 x# Z3 N$ i0 s H+ X% Jint platform_device_add(struct platform_device *pdev)7 [2 D4 e' k0 g& l7 L
{
% M# r3 e2 @' r int i, ret = 0;; i" e) b$ s0 s; Y H2 a% x9 V Z
6 U$ g+ w. x T; i3 t: W/ m" E, Y% h6 l if (!pdev)) T& V) M1 J* l
return -EINVAL;
0 l- |# t8 Q. {+ y
* Z% ]# B9 J2 J6 c! ~3 N1 X2 M if (!pdev->dev.parent)$ q9 N+ H W/ q; p' @# ?! ?; k
pdev->dev.parent = &platform_bus; /*该设备的父设备是platform设备,/sys/devices/platform*/
( ~+ o8 o/ t. H/ C0 P0 t
% C! V; a2 {' r5 T9 ` pdev->dev.bus = &platform_bus_type; /*设备挂载到platform总线上*/
/ ?8 [" _+ W' [! q
. O1 o. j0 n' H! K( `+ y if (pdev->id != -1)9 L4 c2 X: i v2 ]# C) m8 {: u
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
: L/ y2 |/ d1 \# ~: m else
# {$ I9 ?3 H7 S' `; c dev_set_name(&pdev->dev, pdev->name);/*pdev->dev->kobj->name = pdev->name*/
$ f; d" C7 t, V) j; N! Z, C/ r* V. c
( u" S E$ |- N: K6 v: c /*遍历平台设备的资源,并将资源添加到资源树中*/
4 G, \) L A. g1 |+ j for (i = 0; i < pdev->num_resources; i++) {; T# d1 b; {( ?% |" }
struct resource *p, *r = &pdev->resource;* w6 e. k" N6 v! ^+ H4 e1 k
1 K" j) C( ]! n K. i
if (r->name == NULL)1 x# r$ T) C7 j
r->name = dev_name(&pdev->dev); /*获取dev->kobject->name*/
; U' F7 o @" {/ h) A d
+ T& N0 S4 Y4 D$ {" s. i p = r->parent;
8 \8 c0 z4 [! @; t5 J if (!p) { /*p空*/& s& U; V- Y9 g
if (resource_type(r) == IORESOURCE_MEM)
% \/ i3 ~( O; E1 v p = &iomem_resource;+ D( ]% @, ]! j9 X; T* X4 A
else if (resource_type(r) == IORESOURCE_IO)) K. M+ U4 ^7 g) M
p = &ioport_resource;7 ~, ?6 `( s5 Z! d
}
/ \5 ^/ A/ U' q% N3 H1 `/ p) }: I/ d
if (p && insert_resource(p, r)) { /*将资源添加到资源树中*/
& ]: a4 i! A" L) z% T2 H9 ]; y: d0 J" H printk(KERN_ERR+ d/ C( H8 m7 l) ^$ y- k
"%s: failed to claim resource %d\n",, U$ ]: A2 y6 ^2 u, l+ d4 V" f( p* _
dev_name(&pdev->dev), i);% I0 e; P) s( }/ C2 L* @
ret = -EBUSY;
+ T; R$ E. h1 K/ X goto failed;
( i% q8 x0 i% j1 E' Q. H }9 A) s3 i/ W$ X! y
}
$ J! U1 }- C! p! [ ?6 l6 ]$ ] }: B* U
pr_debug("Registering platform device '%s'. Parent at %s\n",1 o0 G& O9 `. B/ z# v* i
dev_name(&pdev->dev), dev_name(pdev->dev.parent));) L- ]/ U5 `8 k# R6 a
6 X+ t0 b8 [/ {( Z" d" _, `: Q, k ret = device_add(&pdev->dev); /*添加设备*/; X8 a% @, i0 w. E2 Z" D( q
if (ret == 0)% O+ P2 i5 r4 v+ @, h3 C/ u
return ret;( r+ Y1 i7 P. Y
) `# l( {" j, t+ p1 L- O8 | failed:
1 d5 d- f9 N7 p1 a4 m* U- B7 O/ | while (--i >= 0) {/ c0 s2 a) i9 x* r* N
struct resource *r = &pdev->resource;1 [# d3 A3 K2 _9 w0 I# ~
unsigned long type = resource_type(r);
3 z- c3 v( ]8 `, I$ I4 X+ v* m4 v' M# K( O( ?& G
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)6 k( t4 {3 r; G% L, a
release_resource(r);3 q7 C4 {( N" F+ t
}" X5 Y2 w- \- B& b: q/ ^
: t% b" r5 V: e5 I% u return ret;
! [1 c2 p$ l% t9 M}
% A, J3 X& e. Y3 ?' cEXPORT_SYMBOL_GPL(platform_device_add);3 k0 X) e$ E8 t1 U! e8 |
+ [, {4 S- {3 E7 R$ d5 u& X
在这个函数的最后赫然出现了device_add函数。我们回忆下在6.1节中device_register的注册过程,该函数只调用了两个函数,一个是device_initialize函数,另一个就是device_add。
- w% ~; k$ V8 a6 X: P本节的platform_device_register函数,首先也是调用了device_initialize,但是随后他做了一些其他的工作,最后调用了device_add。/ i& N' P: q' i9 t( Q% ] L: q- M
+ @& K/ U, k6 j那么这个"其他的工作"干了些什么呢?
: f( o1 ?: B( z0 Q i4 F' E' X3 J- Z3 w# d5 n
首先,它将该SPI主控制对应的平台设备的父设备设为虚拟的platform设备(platform_bus),然后将该平台设备挂在至platform总线(platform_bus_type)上,这两步尤为重要,后面我们将看到。/ q2 q' O! v2 |" I! D* m4 W
然后,调用了dev_set_name设置了pdev->dev-kobj.name,也就是该设备对象的名字,这里的名字为s3c2410-spi.0,这个名字将被用来建立一个目录。6 d8 @' ~2 B3 V& e
+ Z) Z# Y7 U+ C2 F/ z最后,将平台的相关资源添加到资源树中。这不是本篇文章讨论的重点所在,所以不做过多说明。
+ C3 N# O/ v* y$ x: J6 `5 [. {+ ^6 A0 ^6 Q4 }) T
在"其他的工作""干完之后,调用了device_add函数。那么后面的函数调用过程将和6.2小结的一致。/ P( Q; Q4 f- q- m
/ E9 \. _; I/ \由于“其他的工作”的原因,实际执行的过程和结果将有所区别。我们来分析下。
7 Y# ?8 o0 q+ d0 U0 N) T- h3 ?
4 W. T' z, |: Z) p3 Z+ w: n6.3.3 不一样device_add调用结果
7 Q& D8 U* H4 h' ^; o& h4 ?6 E# i- V首先,在device_add被调用之前,有若干个非常重要的条件已经被设置了。如下:7 r$ {, p( e6 U2 p
w" |/ c( Y5 k* s9 u; p- R% N$ B
pdev->dev->kobj.kset = devices_kset x! e* b' N" U; y' Z7 |0 X8 p
/ X D' j5 E/ {( c' @0 N+ apdev->dev-.parent = &platform_bus
8 i8 l6 `3 s! R- C2 v* O4 K2 _7 K& \' t% B- b
pdev->dev.bus = &platform_bus_type5 N9 A9 @2 _2 d, H' L2 I
1 a/ e+ Z2 G* N/ _set_up函数执行时,由于参数parent为&platform_bus,因此最后将设置pdev->dev->kobj.parent = platform_bus.kobj。平台设备对象的父对象为虚拟的platform设备。 ~" |2 n5 Z1 V H$ E
2 r$ U3 C* S! s
kobject_add函数执行时,由于参数parent的存在,将在parent对象所对应的目录下创建另一个目录。parent对象代表目录/sys/devices/下的platform,因此将在/sys/devices/platform下建立目录s3c2410-spi.0。4 z( u, e; ^+ ?
) m# k; I, `. E" r
device_create_file建立属性文件uevent。5 l8 O4 U6 ]! ^' |
bus_add_device函数执行时,由于dev.bus 为&platform_bus_type,因此将建立三个symlink。
" q* d6 r, R: A0 k
M3 W# i) s. N /sys/devices/platform/s3c2410-spi.0下建立链接subsystem和bus,他们指向/sys/bus/platform。* S+ J5 d$ u2 @/ N) {# [
: A# i) ^$ _3 f /sys/bus/platform/devices/下建立链接s3c2410-spi.0,指向/sys/devices/platform/s3c2410-spi.0。
; f$ n: Q& q0 M5 L
3 \9 {6 M1 j4 @* \dpm_sysfs_add函数在/sys/devices/platform/s3c2410-spi.0下建立子目录power,并在该子目录下建立属性文件wakeup。
+ B# P: `8 m, O" {( L
0 p+ H C! e( q2 k执行到这里时,sysfs已将内核中新添加的SPI主控制器平台设备呈现出来了,我们来验证下。* ~4 Z( I. k- w
! Y% q- q. f1 ]- x
[root@yj423 s3c2410-spi.0]#pwd/ i* k, h1 g/ d2 o5 F( p3 ]( I
/sys/devices/platform/s3c2410-spi.0& ?0 c0 c) c9 {5 s2 A
[root@yj423 s3c2410-spi.0]#ll
" q; r, }, {2 Nlrwxrwxrwx 1 root root 0 Jan 1 00:29 bus -> ../../../bus/platform
" f. u6 D: f6 M. P7 s, Glrwxrwxrwx 1 root root 0 Jan 1 00:29 driver -> ../../../bus/platform/drivers/s3c2410-spi
8 |& M# ^: f U. M9 }-r--r--r-- 1 root root 4096 Jan 1 00:29 modalias
, f+ q# A" c+ i& ~drwxr-xr-x 2 root root 0 Jan 1 00:29 power7 A2 }/ z' V5 a* C6 H" t
drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.0
1 P! X- s5 D' r/ |* ^drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.1
9 Q5 V( Z. n( o; i- c, U' U# {& a5 k* Blrwxrwxrwx 1 root root 0 Jan 1 00:29 spi_master:spi0 -> ../../../class/spi_master/spi0
# }: b j: t8 W! k4 s4 clrwxrwxrwx 1 root root 0 Jan 1 00:29 subsystem -> ../../../bus/platform
( ^3 I& m `+ K) _/ \-rw-r--r-- 1 root root 4096 Jan 1 00:29 uevent& M& ^( y. x t0 R" _% \6 ^ \/ H" p
5 s3 Z! `! _; o' L
[root@yj423 devices]#pwd& P+ b% S" X/ b2 z
/sys/bus/platform/devices7 I: N Q( ~& `1 Q
[root@yj423 devices]#ll s3c2410-spi.0 ; t; i& c/ y( X! _$ g
lrwxrwxrwx 1 root root 0 Jan 1 00:44 s3c2410-spi.0 -> ../../../devices/platform/s3c2410-spi.08 K! a+ Z9 ~1 I
通过sysfs将设备驱动的模型层次呈现在用户空间以后,将更新内核的设备模型之间的关系,这是通过修改链表的指向来完成的。
" K8 B1 y" V6 J9 D) @2 U" ]$ X1 e
bus_attach_device函数执行时,将设备添加到总线的设备链表中,同时也会尝试绑定驱动,不过会失败。. f. D% ^- O6 K& `
/ @$ e. u) B O) [
接着,由于dev->parent的存在,将SPI主控制器设备添加到父设备platform虚拟设备的儿子链表中。
! ^3 @& Y: ?& K @( Q3 Q: i7 _ p2 l* k' v# B% Q# q
7. driver举例
) P( {* p/ d1 r& `" |% D% ~我们已经介绍过platform总线的注册,也讲述了SPI主控制器设备作为平台设备的注册过程,在本节,将描述SPI主控制器的platform驱动是如何注册的。
5 r7 X7 n7 y& O8 w8 z- X: S( ~/ G- I; i+ ~
7.1 s3c24xx_spi_init
5 ~; X( H" k* k下列代码位于drivers/spi/spi_s3c24xx.c。) G. w' a# t2 r2 [; {1 P
MODULE_ALIAS("platform:s3c2410-spi");
0 [+ N3 e; z: d8 ^; Y- ^static struct platform_driver s3c24xx_spi_driver = {
; B) b- ~: j1 ~. v! w .remove = __exit_p(s3c24xx_spi_remove),
0 ^4 ~! c9 ~* G& o5 U .suspend = s3c24xx_spi_suspend,) Y: l8 c4 `4 X5 K
.resume = s3c24xx_spi_resume,
# p% b' X, d4 n3 T& n" E# D .driver = {$ ^- [6 }$ E; G% \9 {: y
.name = "s3c2410-spi",
9 ~# u7 C3 q! x h .owner = THIS_MODULE,
7 L& Y3 {7 e1 @. Y. e },
* B; O8 m }* p7 p};* l! `: R# K+ v" X$ `& U M
/ A' }, g* Q9 y0 q5 r3 D. w5 z
static int __init s3c24xx_spi_init(void)
6 o" x% F( x( X7 P$ Y: Z{* T: e4 o7 Z5 _! Z1 |1 x
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);//设备不可热插拔,所以使用该函数,而不是platform_driver_register9 c* l7 m/ a P- d$ s6 m! Y
}$ L6 p: c1 K+ ~9 h( h1 r
驱动注册通过调用platform_driver_probe来完成。
: D$ h5 O7 a( @0 V7 Z2 E注意:driver.name字段使用来匹配设备的,该字段必须和6.3节一开始给出的pdev.name字段相同。
o- l4 c [/ Q/ o/ c T7.2 platform_driver_probe8 l, w Y5 P+ X |; \% j' ~2 z% C
下列代码位于drivers/base/platform.c。
: d6 a$ `- N4 {$ f/**
$ Y9 f" Z% {1 Y6 E! |& V3 L! N * platform_driver_probe - register driver for non-hotpluggable device. f7 V; H/ E8 @
* @drv: platform driver structure
) {; m/ B/ {& `/ Y2 H6 d0 E" h; j * @probe: the driver probe routine, probably from an __init section
" u- a+ }! ]' `6 j# r *' k, r( I2 U3 Z0 e* ?" w% a% p
* Use this instead of platform_driver_register() when you know the device
0 {8 U! h) G$ x6 Z& V * is not hotpluggable and has already been registered, and you want to
" Q$ s* }, @1 O9 o) f9 O' g% W a4 j * remove its run-once probe() infrastructure from memory after the driver! Q2 D) B; U/ p
* has bound to the device.
! Q( w# O1 ^3 u" X: }- W' b# H; s% u *
3 g& i/ }$ V% |/ [9 s * One typical use for this would be with drivers for controllers integrated2 M' o9 [8 H- `' e- m' w; F& M
* into system-on-chip processors, where the controller devices have been
) Y4 p5 c; i3 Z" K7 U * configured as part of board setup.
0 j" c. `9 r) d* o- W/ h; Q) B7 M" T *- k. H, I, r" `& A+ r
* Returns zero if the driver registered and bound to a device, else returns3 {1 `' n/ n5 J9 E! _* y
* a negative error code and with the driver not registered.0 g' i5 d9 u7 _- p
*/! k+ `: Y& j# m8 Z y
int __init_or_module platform_driver_probe(struct platform_driver *drv," K# y- U0 Q7 b5 j) f
int (*probe)(struct platform_device *))
" L- L8 m6 b$ O$ z{, j1 d+ _/ v2 W3 A
int retval, code;+ Z& d% _/ W N# M
" n7 I9 C4 j$ [
/* temporary section violation during probe() */; M! p& q, V' B6 D# `1 K' E8 g& F
drv->probe = probe;
6 \ g: D7 ?/ t$ [) V7 K4 q retval = code = platform_driver_register(drv); /*注册platform驱动*/. R/ t; I; b* u) `1 {( T
8 Z" n; o0 f' f( \" S5 a
/* Fixup that section violation, being paranoid about code scanning
- f- r& B- S+ L* A% x * the list of drivers in order to probe new devices. Check to see
X* `' r8 Z! a8 u( r * if the probe was successful, and make sure any forced probes of& b- x; r" \1 B
* new devices fail.
5 q/ x+ b$ ]6 E6 U; d) r */
0 q7 ~9 E" Q5 J- ?5 Q9 j: C spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
$ H/ i3 t( a$ J7 Y* d$ s drv->probe = NULL;6 n ]6 U D, s* y
if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
" e+ A- R1 T- H! e retval = -ENODEV;
) B* G; m$ Y/ s! r7 p% A. e drv->driver.probe = platform_drv_probe_fail;
5 h2 C7 W! o$ s3 B: Y spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);5 X% Q( ]& R3 }9 {. K( H1 W
+ o% H: h. ~" L; q; Z/ l, M" O/ I if (code != retval)
: X, u6 \: K+ f" u, \4 I platform_driver_unregister(drv);2 l y" [* r, R$ x) w) r
return retval;
& X# v! ^6 H' V* S6 m8 @! {' g}
7 S& i% ~3 f) u( w) a7 LEXPORT_SYMBOL_GPL(platform_driver_probe);
2 E: G/ j$ b5 _6 d这里的重点是platform_driver_register,由它来完成了platform驱动的注册。
& @. ~, b: e J. V( P+ u- ?7.3 platform_driver_register
. f( ?6 s0 G, F0 w4 j; W8 P" Y) D/**
* h/ G/ K0 m( U" _ * platform_driver_register5 B8 Y4 W, m8 J g" m4 v9 [# H6 _
* @drv: platform driver structure
1 B5 Z, p) W& y5 l# j) y( j */
3 w& T: g" O# E, ^1 w) U5 wint platform_driver_register(struct platform_driver *drv)
$ ?& k, H& @4 q, N. T0 K9 f{
9 s: r4 u8 q0 R; } drv->driver.bus = &platform_bus_type;4 M7 H9 _* M& b4 V+ U: [! v; q0 Q
if (drv->probe)
5 N, ]" `+ ]2 ]9 ? drv->driver.probe = platform_drv_probe;
# {, j+ f! K0 Z9 r; n if (drv->remove)
- J8 O% |8 d7 a% T+ C6 v5 w+ h4 Y drv->driver.remove = platform_drv_remove;/ A: P0 c2 Y* }# O# ~- g/ c! a" E/ F9 M
if (drv->shutdown). F3 G1 E0 k5 T9 {
drv->driver.shutdown = platform_drv_shutdown;9 D0 E& Y y! x3 W5 I
if (drv->suspend); w; I5 n w$ h- S" Q/ a
drv->driver.suspend = platform_drv_suspend;
8 X! h) x' G" c; w G2 E k R if (drv->resume)
! f1 b! e2 L' n drv->driver.resume = platform_drv_resume;
$ y6 U4 x, }5 P( n1 { return driver_register(&drv->driver); /*驱动注册*/
0 v3 ~ z3 r+ E3 S6 |}
6 i1 q7 J5 T6 T7 k8 P% a) J5 LEXPORT_SYMBOL_GPL(platform_driver_register);7 f- Z8 d2 l- M, J& M, Y
, ?% o7 l4 Y5 o, f& Z ^& W" odriver_register函数就是driver注册的核心函数。需要注意的是,在调用函数之前,将该驱动所挂载的总线设置为platform总线(platform_bus_type)。
, v" G7 W) H5 I6 t' }4 V" c7.4 driver_register
) N4 s9 g/ u$ E下列代码位于drivers/base/driver.c。
2 R4 S2 q. e0 \# @/ V: X y; k# J/**
+ D: k9 P' ?! {' H/ O * driver_register - register driver with bus
& U5 ~. w4 T" Y. k6 o/ I * @drv: driver to register
+ r+ i n& E( T* e0 ^3 O \ */ V2 c8 J3 R; V, G2 }" X
* We pass off most of the work to the bus_add_driver() call,
$ X* h& w" \) s, C4 q- k; q l; D2 f- ~ * since most of the things we have to do deal with the bus4 [% u# [/ l, Z/ d# K1 K
* structures.
9 Z. n- `5 w. E, c ^ *// H% D" n# ~. M
int driver_register(struct device_driver *drv)
9 g+ o+ k) O' h% k8 S+ v. ?1 m{
8 |) l- h( h$ O; Q1 J* q( U int ret;% C$ ~8 K( e! K a
struct device_driver *other;
% Z( z. b) I8 l' g4 w/ C
. U5 n9 D9 f3 \0 e' E BUG_ON(!drv->bus->p);9 L$ R. U% X- |+ y
" I/ `/ g' V9 }- t& r1 I v7 W
if ((drv->bus->probe && drv->probe) ||2 G! u% Y7 Y; t
(drv->bus->remove && drv->remove) ||5 A$ l/ f% x5 W5 x6 R
(drv->bus->shutdown && drv->shutdown))1 g" ^5 g# G: z
printk(KERN_WARNING "Driver '%s' needs updating - please use "
0 }+ \# Q* l- N/ i* o+ L "bus_type methods\n", drv->name);
: t* H# \: E% ~; u* a# j% U( g6 C$ o1 ~+ t- X3 N" ?/ O
other = driver_find(drv->name, drv->bus);/*用驱动名字来搜索在该总线上驱动是否已经存在*/! Y) _1 L" {7 c2 u
if (other) { /*存在则报错*/
( t# ~( n t) Q7 ~5 R4 Z0 M put_driver(other);
- r, a+ O+ J, A9 f! C+ H printk(KERN_ERR "Error: Driver '%s' is already registered, "
- o: j, c1 ]& ^ "aborting...\n", drv->name);4 }/ k3 N: r; f3 l9 N/ ]4 ~
return -EEXIST;" E; J6 |* h1 e; j* L" B _0 }
}
; l) I/ m$ X7 C" Q: Y% V5 ]/ s2 V4 J9 B1 _) |- M+ i
ret = bus_add_driver(drv); /*将驱动添加到一个总线中*/
" t4 a' @& i8 S& j if (ret)
6 B. {4 d2 @( e5 R return ret;
- K# D1 T m1 c# j ^ ret = driver_add_groups(drv, drv->groups); /*建立属性组文件*/
6 b" s6 ^2 }5 j: ^0 I; s7 V if (ret)
. Y* `, J% v% B& B4 b% l3 M bus_remove_driver(drv);
# m: }! v2 c( d' E+ g; | return ret;1 W5 j1 H7 E3 W" P
}
7 ]8 ?5 U8 r$ d5 M7 }3 j9 REXPORT_SYMBOL_GPL(driver_register);
6 _( n! }5 c: O& K* ~+ P) L1 F( h' c这里主要调用两个函数driver_find和bus_add_driver。前者将通过总线来搜索该驱动是否存在,后者将添加驱动到总线中。
' J& u- \- ~3 d. j. a接下来就分析这两个函数。
5 ?) p3 B, c) z. {8 n. x7.5 driver_find4 t! h& ~. m8 C+ M; ]
下列代码位于drivers/base/driver.c。
/ } b; [4 T- U. J/**& h" H! A. ^& J# r
* driver_find - locate driver on a bus by its name.' S( {8 J8 d# W- _8 ^
* @name: name of the driver./ v# J% N5 Z/ h/ @) @
* @bus: bus to scan for the driver.% G& t" }& O# o& u7 r
*
* ^( G m" e( [0 s6 q( |/ w; z4 k9 x * Call kset_find_obj() to iterate over list of drivers on( I$ V+ T: W& m1 E# {
* a bus to find driver by name. Return driver if found.
, @) Y2 _4 i4 s. X o7 f" v *
$ b' O% [1 K0 r+ b& @% R# F' ? * Note that kset_find_obj increments driver's reference count., P E( c1 p& ]0 U
*/( R( c, b: Q) }, y' L" o% n
struct device_driver *driver_find(const char *name, struct bus_type *bus)( w# t$ q% x: A' a. ^
{
- `$ P7 l$ _7 j1 S& k( S struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
/ v4 i: ]3 K) k, p7 \/ e6 z' i, e struct driver_private *priv;1 j+ I- L! p/ _# S" p
& s* p/ _8 ]' B" q' l
if (k) {2 z. \& f, a! F( C1 V
priv = to_driver(k);
) \- x8 d- K+ G( G8 [ return priv->driver;
) O. H' J" q) Q0 T6 `" ^ }
: N! q6 r, N/ s return NULL;& n d" y Q- l
}4 ^* c" T9 i+ Q7 B9 L
EXPORT_SYMBOL_GPL(driver_find);. T E9 n: a# p9 b* [
. X: U3 Q5 E( h. Y& I% {7 A
/**8 C. ?8 y8 f6 B$ Z2 v6 q
* kset_find_obj - search for object in kset.
8 A; h7 o- \/ V Q) L7 W! H3 R * @kset: kset we're looking in.
/ ]+ H: d; G1 I0 D1 y( ^ * @name: object's name.
4 i$ v" |# Z x8 ^3 s/ i" X *
. S) y- e6 c" Y2 n/ S* B * Lock kset via @kset->subsys, and iterate over @kset->list,, ^& T H& [ J3 k2 _
* looking for a matching kobject. If matching object is found7 }* @) P6 E0 N& O* ^" Z3 i4 M0 b9 F
* take a reference and return the object.0 a0 f: b/ M2 H, X: u. B
*/( I. j& e0 \. z0 e4 b
struct kobject *kset_find_obj(struct kset *kset, const char *name)
! e2 p# N& K, v; u1 `7 `' ~# U# I{/ f& j" ]+ I9 w, V- Y; S T6 {
struct kobject *k;
8 B; B/ c, F! G' O) e8 @* N struct kobject *ret = NULL;
9 Z* W) e/ `/ ^
1 ?& k f. K: O' n& l spin_lock(&kset->list_lock);% C# q& i( L0 N& m9 I% h" | z0 ?
list_for_each_entry(k, &kset->list, entry) {
* Q, V9 Q& i. ~4 p* W if (kobject_name(k) && !strcmp(kobject_name(k), name)) {$ c5 Y* p1 a6 t
ret = kobject_get(k);
8 I/ n0 x8 p1 g% F Z8 P break;
7 a8 k/ j# a" g) K2 ` }& z$ j$ w" g% F6 I) A2 `
}
9 L) I- V1 _* g# o spin_unlock(&kset->list_lock);
8 C3 X" q6 |; D( E1 C5 Z return ret;
" j: N6 L2 w5 C5 l2 A}
& W ^: Q1 L5 K( f这里调用了kset_find_obj函数,传入的实参bus->p->drivers_kset,它对应的就是/sys/bus/platform/下的drivers目录,然后通过链表,它将搜索该目录下的所有文件,来寻找是否有名为s3c2410-spi的文件。还记得吗? kobject就是一个文件对象。如果没有找到将返回NULL,接着将调用bus_add_driver把驱动注册进内核。
. z/ `/ D% A2 \5 j2 G7.6 bus_add_driver
3 Y$ l G W4 m% J T9 a下列代码位于drivers/base/bus.c$ h1 ~0 o. B' x0 P
8 ]' `. Z" j3 I. }/ N# Z, J" |/**
: V8 t( R7 I4 ?$ p3 ~3 O * bus_add_driver - Add a driver to the bus.
4 b8 U' g9 i1 y) |1 W- ?$ }) P * @drv: driver. O1 E" K3 L6 I
*/. w$ j! z6 s7 x2 V+ n! r9 Z! X! [) r! N
int bus_add_driver(struct device_driver *drv)
6 O& C/ k" t: V2 C{
. H3 }5 l! O" V7 {6 \6 _8 `8 | struct bus_type *bus;) _0 s- t: @+ t, K r# r$ V0 }
struct driver_private *priv;
1 r3 w0 r0 Y; V9 x6 T; I3 m int error = 0;4 q+ e- i) S& W
4 \; R+ i8 [1 Y+ x+ U% H, u bus = bus_get(drv->bus); /*增加引用计数获取bus_type*/9 b- \+ v5 S9 z5 N# R' @4 f
if (!bus)
, F: \4 Y2 X4 C. Y. b return -EINVAL;
% o2 F# _8 Z. g+ i8 j
2 ?4 Z1 m& k* B' ^4 |5 ? pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
+ T% T6 Z% Q+ l
- n$ n# T! ]/ P- r+ t! o# h3 F priv = kzalloc(sizeof(*priv), GFP_KERNEL); /*分配driver_private结构体*/5 ~; W& p5 l$ D- h) A, C7 C
if (!priv) {& |6 }+ P# S# S6 M+ h
error = -ENOMEM;
/ d7 s. ]/ a$ s! C. x/ ? goto out_put_bus;% `3 ?" T V2 p
}
4 v6 Z. }; \. m I4 B /*初始化内核链表*/( j" N: V0 H* j
klist_init(&priv->klist_devices, NULL, NULL);# ?( k! K% ]- Z A, m2 i$ U- W8 f
/*相互保存*/+ ?! U# G1 c6 i) D5 G r
priv->driver = drv;
/ x9 y6 x: y+ _5 c. d7 U* [* Y% f$ y drv->p = priv;
" ~ N" B( m/ K2 f! @" \! d /*设置该kobj属于那个kset*/7 j2 A" ~, q) \; k! C# \
priv->kobj.kset = bus->p->drivers_kset;
) `5 L' b, W! @0 r6 ?" X error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, /*parent=NULL*/9 e! C. {0 ~/ V1 s" O* Q+ t
"%s", drv->name); /*执行完以后,会在bus/总线名/drivers/下建立名为drv->name的目录*/
$ i3 H# N# [1 j+ y2 f if (error)2 D4 R" ]% x" n# Z; W) T5 z7 Y
goto out_unregister;4 E) j- h. t2 g4 x
7 N9 ~) X$ s0 s4 l if (drv->bus->p->drivers_autoprobe) {( q! X& `5 v5 c
error = driver_attach(drv); /*尝试绑定驱动和设备*/* a4 G; K( v/ V3 B3 h) m% s2 I
if (error)5 s# T' s- ]% J
goto out_unregister;. Y; l: C* c; L8 m: Z: ~* F
}
) d+ i0 G' `' q8 a/ P, C1 H /*添加该驱动到bus的内核链表中*/
- \# P/ y0 b) S9 U- b5 D klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
6 \5 P. S+ S2 }2 B" G module_add_driver(drv->owner, drv);/*?????????*/
+ i7 m0 |. R" _! S8 b, |9 P+ O" n! _" _
/*创建属性,在bus/总线名/drivers/驱动名/下建立文件uevent*/
6 ~" h: f3 Q( s& `7 u error = driver_create_file(drv, &driver_attr_uevent);
# P' a' c0 i6 b5 p# h0 r7 l if (error) {( H: u! ]4 ^. } L2 e
printk(KERN_ERR "%s: uevent attr (%s) failed\n"," h# j* P, B& O
__func__, drv->name);1 D' ~3 h' x- H! [
}
* O+ x) g- A5 p4 ?( C, _& v /*利用bus->drv_attrs创建属性,位于bus/总线名/drivers/驱动名/*/$ x4 W/ N5 k+ {* q. q# u
error = driver_add_attrs(bus, drv);
0 D/ e9 S+ R# }2 z if (error) {
% W0 g% u+ i* V5 N9 U7 ^: T6 v4 Q /* How the hell do we get out of this pickle? Give up */ `5 f0 A& N7 g2 j
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
# m9 P- z) Q. O# R7 k __func__, drv->name);
* a% U$ [1 Q3 o' P3 `3 `2 V6 E }8 F5 q! u2 \" Q8 Y# N" B5 {+ U
/*创建属性,在bus/总线名/drivers/驱动名/下建立文件bind和unbind*/& m' E: g2 R( X6 P* n
error = add_bind_files(drv);
( `7 D% V# d* z: v& ~* e if (error) {# e4 m8 i8 ^3 d1 m1 B7 r: j, [
/* Ditto */
) a7 O( Y% y$ w3 x% ~* N printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
# p- V; {5 v2 Y- u1 Y( Q __func__, drv->name);$ [. m3 x% `* j8 B/ e$ E
}6 J( z0 ~. b( \1 A: [- \$ e
/*通知用户空间???*/
* j( ]6 T' a$ i kobject_uevent(&priv->kobj, KOBJ_ADD);& I0 w; F6 u1 r3 v2 N
return 0;: @. a6 o \9 p( Q% I( U$ @
out_unregister:
. k z! C( m; P: ^+ x kfree(drv->p);
E/ o6 J# ?& p# W b4 s drv->p = NULL; u$ m: U+ e( z/ E
kobject_put(&priv->kobj);9 S+ p( J: ]7 ~9 E' q9 G( R
out_put_bus:# {8 W6 O; }; T8 w
bus_put(bus);
% W( G6 K5 N9 c5 E9 Y5 v# ~ return error;
0 d# i9 S- s3 C& b" ]" M% |}
; N0 ~; p4 J# z! y# n1 n在设置driver的kobj.kset为drivers目录所对应的kset之后,调用了kobject_init_and_add,我们来看下。% g, ^3 q- s0 n, B4 k0 J
7.6.1 kobject_init_and_add0 _7 q% V/ }5 Y: ^) t( N
下列代码位于lib/kobject.c。
, L# i; K% R$ n- h/**
1 H- j, ~0 C. [7 r6 O * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
|9 e5 B5 v/ ^0 X% ~% D * @kobj: pointer to the kobject to initialize
2 V) n0 z* ^/ _' w * @ktype: pointer to the ktype for this kobject.
8 y: Z. p! ]2 n+ i% } * @parent: pointer to the parent of this kobject.
$ g, k( L" q, `6 N& T, Z* F* Q4 w * @fmt: the name of the kobject.% E' c, ~4 |! W7 ^' c
*
9 L! b8 o8 ^1 d. q( _* Z+ h * This function combines the call to kobject_init() and- b0 G: t1 D D* J! C
* kobject_add(). The same type of error handling after a call to
. S8 N& G) o7 Q9 s% ~8 E2 F * kobject_add() and kobject lifetime rules are the same here.. t! u. D% x+ j% H$ O2 N* y
*/6 Q0 V1 s( {+ f+ l0 X5 m% S; f- k
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
/ J- L" N. v& H+ C9 j struct kobject *parent, const char *fmt, ...)
7 t9 N+ Y/ k2 D% ]) v7 L& R# L{- R% G% P+ a# ]2 @3 P1 K$ d
va_list args;2 ^& B6 V; r. E: F
int retval;
4 p* n m& G$ b3 p6 R+ F' f. @# m/ B& N0 u% Q
kobject_init(kobj, ktype);1 {$ m" P; E4 V- V' l2 J
/ p+ j% y# C/ e0 d
va_start(args, fmt);/ { F7 c' u& B X% T
retval = kobject_add_varg(kobj, parent, fmt, args);3 R5 C- i3 ^0 k2 O4 P4 u. ~! r s
va_end(args);( Y6 ]9 U, k: H( Y
) F9 b& S! i' f% r6 a, d return retval;
' p9 O) P* g0 G* t" A/ g- d}
* m5 D$ i7 C3 ?" M! t0 cEXPORT_SYMBOL_GPL(kobject_init_and_add);
/ e2 u9 T. Z& m& k该函数中调用了两个函数,这两个函数分别在6.1.2和6.2.2中讲述过,这里不再赘述。, V% E, [9 G( Q0 A2 S; w
调用该函数时由于parent为NULL,但kobj.kset为drivers目录,所以将在/sys/bus/platform/drivers/下建立目录,名为s3c2410-spi。6 |' h. Y. _9 D1 E1 b& u" s
! m- N2 h0 k( Q/ }( |; u% s6 C我们来验证下:
# ?- O* S, D2 N! x7 `
/ _. G7 V; b$ y- g4 \4 T# H# o[root@yj423 s3c2410-spi]#pwd3 g4 w% h$ I7 m! I7 B. ~8 p( p+ E
/sys/bus/platform/drivers/s3c2410-spi1 q( ?% H1 Z: { s
接着由于drivers_autoprobe在bus_register执行的时候已经置1,将调用driver_attach。
5 @2 W9 L# }" r8 |, h
) ?9 Q! O# P. P% w S) }7.6.2 driver_attach% T4 J! s) M7 c# j; B1 j) Z4 y
下列代码位于drivers/base/dd.c。6 v* z# s1 W7 V1 C3 |. w
/**8 r* @8 G$ W* J; @( [' |
* driver_attach - try to bind driver to devices.* u0 \5 y, y* H3 A7 R
* @drv: driver.
1 B* ]; M l# q) U) H2 L: v *
' C6 I# I- G1 M * Walk the list of devices that the bus has on it and try to
6 n% q; X/ T+ K1 G * match the driver with each one. If driver_probe_device()
: w1 J# I5 e/ Z* Y4 \; e# }7 P* B * returns 0 and the @dev->driver is set, we've found a
9 p- c6 r3 ]6 r9 f: T * compatible pair.4 {3 D! b2 R; Z* l* W8 Z9 l
*/
* m; @9 }8 T( X- Y. a9 a9 fint driver_attach(struct device_driver *drv)
5 |/ u7 b: e1 U2 q' P0 ] G* l{
8 T0 L$ W; {& j8 k6 t! s3 `/ ? return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);/ w1 U! n8 y+ H" R
}4 `2 y( t; E% V8 @9 L, x
EXPORT_SYMBOL_GPL(driver_attach);5 D. `+ b% ^' S- h: l
该函数将调用bus_for_each_dev来寻找总线上的每个设备,这里的总线即为platform总线,然后尝试绑定设备。
: N$ G! H4 f2 Y6 r" v0 F2 j# Z这里需要注意的是最后一个参数__driver_attach,这是一个函数名,后面将会调用它。
: s; S' q8 R. I+ N
" p; n' S+ ]* c/**
% m3 K; |. A) i* n' } * bus_for_each_dev - device iterator.3 }, u) r1 @- k# \
* @bus: bus type.! B6 \) V! y; F; M- l" ?# w
* @start: device to start iterating from.
8 B& u; }: i0 r+ _ * @data: data for the callback.; }+ L8 y# O } N/ k) `9 A( u6 d- M
* @fn: function to be called for each device.
6 `2 h2 U/ v, ` *# n% E6 R5 T! H2 r; S1 b/ V8 d) h
* Iterate over @bus's list of devices, and call @fn for each,
% f) h, @9 p6 B * passing it @data. If @start is not NULL, we use that device to
7 D6 D R" A- t9 } * begin iterating from.
4 q& H) n9 T/ I% p *! v- `" L9 @$ h, ^- f8 T
* We check the return of @fn each time. If it returns anything
; B' X0 [' h7 U3 l' P8 I7 { l, O * other than 0, we break out and return that value.
P& V4 q3 Q0 V) m! V *
" r) d. A# z# I5 @& b7 L * NOTE: The device that returns a non-zero value is not retained$ i2 }0 ]: e! \: U, j' G9 E
* in any way, nor is its refcount incremented. If the caller needs, D3 Q7 w/ M/ u9 ?1 Z
* to retain this data, it should do, and increment the reference
7 E; k2 w1 a0 P3 ^5 _ * count in the supplied callback.: A8 p4 s7 E2 W. ~6 [8 {0 A
*/
+ e) Y$ D% ]) O, a* Wint bus_for_each_dev(struct bus_type *bus, struct device *start,
% g- Y. [+ m( X, x. O5 d( N void *data, int (*fn)(struct device *, void *))9 C4 d1 B* o6 _! B m# {: T
{
2 j+ w8 z1 R5 A6 {; ?! w! V struct klist_iter i;
; W4 C5 c9 r6 L0 f struct device *dev;7 B7 b' @: q7 C, B' p
int error = 0;
5 \7 G* c7 t( b# s
! c$ t4 `6 q2 z$ I1 d0 k! O3 p3 I if (!bus)/ z4 N3 K4 N* W2 j) e2 r' `( G
return -EINVAL;
. e+ y- A' |. y
/ `& Q& D% F6 C& h# u o/ D( ] klist_iter_init_node(&bus->p->klist_devices, &i,6 I2 J" A) j2 \& G. U5 W. G
(start ? &start->p->knode_bus : NULL));
, E7 U# u& C% h: s( J. _8 _% n while ((dev = next_device(&i)) && !error)% r* e2 d- {4 A! c. z8 @! z
error = fn(dev, data);$ D" s# f$ z6 Z! w/ M. n
klist_iter_exit(&i);* ]8 C5 U; {9 w- P- I; T2 g
return error;1 p2 t, U$ d$ Q4 _ h$ \) i
}
# A$ _, r+ A' Y1 T# z5 C2 vEXPORT_SYMBOL_GPL(bus_for_each_dev);
% V; p" ]( {$ r! z. F$ v通过klist将遍历该总线上的所有设备,并为其调用__driver_attach函数。
4 }- S t$ v% Q7 T; A+ k1 B9 e5 Ustatic int __driver_attach(struct device *dev, void *data)7 d. l& C( ^7 F0 `" E
{
( S* Y+ `, |( j& o2 R, j$ Y struct device_driver *drv = data;
9 b: W2 b% p+ f, V7 ?0 S* k
! G; {; U* M- Z2 F /*
4 g; \& p& V1 {/ M! j ], s# n * Lock device and try to bind to it. We drop the error1 e3 U7 D' i) i- k; E
* here and always return 0, because we need to keep trying
$ Y8 C" k0 f8 _% q, Y* b) C" C * to bind to devices and some drivers will return an error
0 [" E6 H$ B3 L" M0 b3 G * simply if it didn't support the device. M4 c8 h* K% ^2 C
*
- g' V' y3 _" O2 |; y% T * driver_probe_device() will spit a warning if there+ l2 _2 ]8 \' x$ I
* is an error.3 d: M: }4 ]7 L0 w5 b
*/
+ }; B0 j) A1 J Z/ K( r! m9 o, o: w+ b$ f0 s# G% _+ ^
if (!driver_match_device(drv, dev))
: r. _4 m w6 n: l( E3 L5 H return 0;1 x% w: O' C' u, _( t/ R
* ?3 a, N" b7 r4 w
if (dev->parent) /* Needed for USB */: B# U F" ~, |9 P* Q5 g6 [. f
down(&dev->parent->sem);
6 n- R7 r) {3 c down(&dev->sem);
' b6 |* a" K) s if (!dev->driver)
( Q! j$ W$ E; H, z driver_probe_device(drv, dev);! B: `+ m, L( ^3 P; T' i" x9 `
up(&dev->sem);7 p* ?3 g& s' _# I
if (dev->parent)4 S8 l* r% A4 K. f, ~
up(&dev->parent->sem);
& |$ ]% C: t) t8 d6 ~0 f
- g0 h+ s( @4 Y9 G7 x return 0;4 t7 W: J2 V& u& H8 x
}
{6 E; u; g. m首先调用了driver_match_device函数,该函数进会进行匹配,如果匹配成功将返回1。我们看下这个函数:
) r7 L. D4 J" t: Estatic inline int driver_match_device(struct device_driver *drv,
1 f( w7 g6 j. |! J6 I/ m7 J struct device *dev)
2 i8 D5 S8 F* y: _( ?{
% E' s, M2 d X return drv->bus->match ? drv->bus->match(dev, drv) : 1;! J/ B$ F" M/ \5 f0 Z2 S) W
}
! o6 V& b. L0 a p% U( |# h3 _ Y2 ^3 [8 L
这里直接调用了platform总线的match方法,我们来看下这个方法。* @/ C7 e0 V) B" e* M( p! t# p" ^( o
/**
9 N! a8 e0 _6 q * platform_match - bind platform device to platform driver.
2 l: {& `) O0 R * @dev: device.
1 P8 i7 v3 s7 Q * @drv: driver.
& k0 s' B! t7 g1 D: g' y *
" G8 I- B; C4 x( C, u3 |5 D * Platform device IDs are assumed to be encoded like this:
5 e2 D8 |" }% ]) U S' O" r6 F. u * "<name><instance>", where <name> is a short description of the type of3 ]5 x$ F' B2 b v. A
* device, like "pci" or "floppy", and <instance> is the enumerated
: ?- I3 M% Y! r9 L- I8 T$ E * instance of the device, like '0' or '42'. Driver IDs are simply
0 l2 I! w* ?5 N9 G5 J5 m' u/ { * "<name>". So, extract the <name> from the platform_device structure,, d S/ R9 c3 c3 x/ N4 x/ Y- h& j
* and compare it against the name of the driver. Return whether they match @- `( P5 I& w# H( [
* or not.
6 Z w( S& E+ o; L */! j4 P% w) {& u4 w/ E( j; S/ ?
static int platform_match(struct device *dev, struct device_driver *drv)
3 P4 i0 J' m/ w: x' Z2 k{
9 B: c! T+ Y4 o struct platform_device *pdev = to_platform_device(dev);+ ^6 k3 `' {1 c+ ~
struct platform_driver *pdrv = to_platform_driver(drv);
9 C* E8 X$ }! W/ j& t, X3 W k; x9 P: |8 O0 l# w
/* match against the id table first */2 V0 ~; `: {- o
if (pdrv->id_table)1 ]6 @! U4 | L7 W
return platform_match_id(pdrv->id_table, pdev) != NULL;" E, S# F3 a& i! c' [
3 k$ B" B/ o) p R" {4 }% o/ T) B* D
/* fall-back to driver name match */) G" F, O! P2 c1 Y4 h3 ?* m5 s
return (strcmp(pdev->name, drv->name) == 0);
/ W7 {, h" r( ~: d' x! E}
' z2 f/ ^3 j' N6 { q. j { a该方法的核心其实就是使用stcmp进行字符匹配,判断pdev->name和drv->name是否相等。7 G( L) q5 t0 A+ e. m
在本例中两者同为s3c2410-spi。因此匹配完成,返回1。
% M% H C5 I4 N% O. T! B6 R' m+ H0 ]
返回后,由于dev->driver为NULL,将调用driver_probe_device函数。我们来看下:9 A' }7 x/ e E5 M' h; J( d) P1 b
0 U& s$ z- Q. a3 U1 w n- V/**' J4 R' G% g/ a. T) j/ z. z: ~. p
* driver_probe_device - attempt to bind device & driver together8 i" r! ^: {) N+ h" j* [
* @drv: driver to bind a device to
, N; }, w0 v+ W' t% ? * @dev: device to try to bind to the driver( j. b5 X/ t3 k% K* z/ R! T0 O; i
*
( c2 ?# i0 ^4 `- d, h * This function returns -ENODEV if the device is not registered,- [/ \8 J6 k$ Y
* 1 if the device is bound sucessfully and 0 otherwise.+ H+ ~& a' o$ T1 s/ S8 b
*; S/ G7 e- E' |% R+ l8 {* _
* This function must be called with @dev->sem held. When called for a- E* y1 Q) e6 Q. J3 n) \
* USB interface, @dev->parent->sem must be held as well.
H" f& ~3 v. G3 q1 P */% X. t0 O6 j/ _
int driver_probe_device(struct device_driver *drv, struct device *dev). b8 u4 ?9 `2 {" Z9 g
{
$ w, Z# U. L9 C- u- O int ret = 0;0 Y& Q7 l0 y4 ~& ~, J; F
% n! h \6 p, S7 R
if (!device_is_registered(dev))% K6 P; {# k6 S
return -ENODEV;( u$ u6 t4 ~ p) H* `
3 J7 |" S6 e- h& \/ I5 M6 L) @
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",: `. i3 ]$ Z# |
drv->bus->name, __func__, dev_name(dev), drv->name);; [! P+ B' |5 R7 L+ q
7 A1 t- O& V5 Y7 t4 v; Z( R$ ^
ret = really_probe(dev, drv);5 h& w9 N& j+ M; k: F* _# O
. r2 f b! E2 S) L2 e9 a return ret;3 a# k: I3 W% \2 M" E* F/ |( |
}5 \1 k9 t, ]/ k- F0 E+ T: b
static inline int device_is_registered(struct device *dev)1 M5 }& s. e8 z0 Z+ r& o
{* M- {; g) f2 k3 T, i
return dev->kobj.state_in_sysfs;
- _/ d o1 h# F9 J3 q2 q}! }$ W. s8 ]% n
该函数将调用really_probe来绑定设备和它的驱动。! x9 g+ K3 l0 W v, z) Y
static int really_probe(struct device *dev, struct device_driver *drv)
2 ^& w# r2 _/ c{" D; j$ |7 F; \$ {; B8 d
int ret = 0;
' b* ]) E+ L* Y; C' L
9 ?7 q$ P/ d0 }2 i! f, |7 G; a. b atomic_inc(&probe_count);: E; e h1 ]% ]+ n0 X
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",; ]' x* B h6 U# V
drv->bus->name, __func__, drv->name, dev_name(dev));1 `4 ~& i6 y% d. @1 R
WARN_ON(!list_empty(&dev->devres_head));2 J* H: L" |& a7 x+ j
$ R g f1 |# | dev->driver = drv;% e6 B" o, ~. | W- t6 J3 `
if (driver_sysfs_add(dev)) { /*创建两个symlink,更新sysfs*/1 v: u" d8 q0 _: \* ^& G
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
8 e8 R" v' S! p, w0 {2 F: g# F __func__, dev_name(dev));. H+ v0 m R4 L, W7 m& G$ U$ ?
goto probe_failed;
9 Q' t5 g- Z( n }( r7 K7 B. ~# J Z, o( E8 K
: b6 o% ]3 L0 L. v, R if (dev->bus->probe) {
# b" O" c4 S6 q5 i5 E' S ret = dev->bus->probe(dev);/*调用总线的probe方法*/
2 \0 }# x( w) Q9 a4 B( v0 W if (ret)
( l$ L q. ^4 L( G4 s7 ~$ L goto probe_failed;* X+ g! D8 W) L
} else if (drv->probe) {! V- Q% Q* O" |+ }' C
ret = drv->probe(dev); /*调用驱动的probe方法*/
" B; y1 |; C9 q6 X' s if (ret)
1 S# B X" J- ] C/ ?, ] goto probe_failed;9 T7 o9 o1 e& @/ N* z4 r2 J' H& M
}# v/ e, t$ L3 k9 ~. ]
6 D* m+ g6 R# L7 X3 q) n( V; r driver_bound(dev); /*绑定设备和驱动*/
8 e: Y& l$ m$ m8 h) y9 J" ? ret = 1;( l) I$ z8 H- [3 p9 I+ T
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",4 v2 N. X/ p5 F
drv->bus->name, __func__, dev_name(dev), drv->name);
3 g2 L; H* P/ h+ j8 x+ M- A. u- ^ goto done;6 E( [7 e+ {9 l
6 Y" L0 F) i5 B2 e4 M: Gprobe_failed:
7 I6 o0 h+ V0 v5 \- b devres_release_all(dev);
, Z3 Z) ~* Q5 m: t" P. ]# X driver_sysfs_remove(dev); M7 q8 G6 I1 ?! a* b
dev->driver = NULL;& Y% K Y1 `% R! b. o# z
, ~" W+ ]8 ]" V5 E$ @
if (ret != -ENODEV && ret != -ENXIO) {
1 r3 {# P% Q; T+ O2 y7 @ /* driver matched but the probe failed */
4 o! W0 z" Y* Y/ M7 _# _0 M0 {1 h printk(KERN_WARNING
- ] y* H+ i9 ]1 }( ?6 Y "%s: probe of %s failed with error %d\n",, M" |+ C4 z$ u6 V) [
drv->name, dev_name(dev), ret);/ a+ _3 t4 `5 G. G: ]+ g% j
}5 o) e L* {9 n& q
/*
2 {0 ^) u# W4 b1 z9 r. H * Ignore errors returned by ->probe so that the next driver can try$ i v. f) ]! U
* its luck.( c: v( {3 B2 k( i" M/ i
*/
$ B* w' p4 d! m1 ` g ret = 0;# C3 v' X& S# M$ M5 q; d; q8 `5 L' v
done:
: Y0 {* }) e/ H4 a! |' I; T atomic_dec(&probe_count);+ t7 L2 V- J- `) J" ^5 F
wake_up(&probe_waitqueue);
; v& a- \: U. a1 a/ P* R return ret;. Q0 q2 f. c: T S; M
}
5 Z* W9 U y! j9 F) W4 k( @; S
0 Z6 H/ K9 f2 t8 ^在这个函数中调用4个函数。
" k3 |2 L( M0 ]+ u# n7 q1 P5 [
/ }7 X2 ^$ m! F6 v) R第一个函数driver_sysfs_add将更新sysfs。
, H* b9 Q: X- u) t; I; _8 i2 D( w9 D
static int driver_sysfs_add(struct device *dev)7 z! o* t6 B4 v$ ]9 u4 U" ]& H
{# s1 i0 z6 ?% |. B, C8 l
int ret;
% |& W1 z& L2 c3 J& g3 W /* 在/sys/bus/XXX/drivers/XXX目录下建立symlink,链接名为kobj->name,
! j5 X8 X( M$ N' v9 s4 l 链接指向/sys/devices/platform/XXX */! B; L$ F5 ?; A) n2 [. `
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
5 W! F$ b) d; Q# Z8 n: `, B b kobject_name(&dev->kobj));
7 l! t5 S6 E% c9 i if (ret == 0) {0 T6 e2 Q5 `3 y0 S5 M. n7 m9 C
/* 在/sys/devices/platform/XXX/下建立symlink,链接名为driver,
4 t! n- v. v o6 ]4 S& T 指向/sys/bus/xxx/drivers目录下的某个目录*/: U# [5 F' h7 e7 c! Q) c
ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
8 V' ^" b: {9 i8 A; l7 R! [3 p+ Q "driver");
l x: o: p$ O$ y( h: p if (ret)& P$ b/ Y- X4 E- Z; g# v, K/ I6 M$ p
sysfs_remove_link(&dev->driver->p->kobj,
2 h, H8 r6 l! j! E' @- B kobject_name(&dev->kobj));
9 n# v8 l1 d1 T* o" h: o% @ }
8 g3 L1 x/ K# ?2 z5 }9 `4 u return ret;
1 \$ H8 A" l+ _; f) p}) O) b4 C8 y* v3 E! l6 ]7 K& w1 H
: Q# Y9 Y, ^2 L0 v. p9 C! S
执行完以后,建立了两个链接。, M7 c' H7 V. K5 a4 H
在/sys/bus/platform/drivers/s3c2410-spi下建立链接,指向/sys/devices/platform/s3c2410-spi.0
8 ?6 F$ U+ d) l; g; m* o在/sys/devices/platform/s3c2410-spi.0下建立链接,指向/sys/devices/platform/s3c2410-spi.0。
: n, \* h6 T" T3 h& v; `5 u# } E这样就在用户空间呈现出驱动和设备的关系了。我们来验证下。
V% b+ P; C4 E; ^: Z* i: O3 B3 `- e3 |# f
[root@yj423 s3c2410-spi]#pwd, h! G7 Y! z6 x) D
/sys/bus/platform/drivers/s3c2410-spi5 T. R& ~ ]8 D6 ^8 m4 Z
[root@yj423 s3c2410-spi]#ll s3c2410-spi.0 $ ~2 s: O6 Z/ @9 e
lrwxrwxrwx 1 root root 0 Jan 1 02:28 s3c2410-spi.0 -> ../../../../devices/platform/s3c2410-spi.0" ~' \+ u/ T- _2 t1 z5 {
[root@yj423 s3c2410-spi.0]#pwd, @3 _, V& }4 _
/sys/devices/platform/s3c2410-spi.0
' J9 R, J, r1 d5 s[root@yj423 s3c2410-spi.0]#ll driver* o6 E, R9 O/ `
lrwxrwxrwx 1 root root 0 Jan 1 02:26 driver -> ../../../bus/platform/drivers/s3c2410-spi
C! p+ M. Z9 { f
. Q, q3 z) q7 }1 l4 i第2个函数执行总线的probe方法,由于platform总线没有提供probe方法,因此不执行。9 i' c' M) }# U4 X( x7 R
- u8 }6 x7 ?) T3 n5 P! x3 [ d! L第3个函数执行驱动的probe方法,驱动提供了probe,因此调用它,该函数的细节超过了本文的讨论内容,所以略过。: Q% g7 d4 G$ O
4 f+ }' }$ K* i4 V3 R0 r3 S& J第4个函数执行driver_bound,用来绑定设备和驱动,来看下这个函数。/ m3 \: W) C, K$ x, u
; e, ^% @" d" h" istatic void driver_bound(struct device *dev)# d. ~! I: y% u: ?4 ?. {. C
{8 c& T% g+ y- f7 K- t9 _
if (klist_node_attached(&dev->p->knode_driver)) {
( R* D8 k7 K* z3 m: e* I printk(KERN_WARNING "%s: device %s already bound\n",
& r" `8 f6 Q) L: g: P __func__, kobject_name(&dev->kobj));1 _1 L( s0 j3 V( g+ q
return;
6 h2 I+ R6 b) i3 {- v5 {0 y8 y5 } }
4 K0 |* f B& Y( w$ \4 t a- D4 D' n2 V. [: ~
pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
; d& x1 z; s' t __func__, dev->driver->name);/ j. N) H+ H& i4 j3 l) {9 R
$ K; Z0 I# B/ K4 f, N) i* [/ j if (dev->bus)2 B- {& H" C* p6 U3 p( X1 Z2 }, R2 }
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
% x* P4 _9 ^) x) D1 T BUS_NOTIFY_BOUND_DRIVER, dev);
! j% I" D6 a! L( X- b
8 f8 E+ P, j+ e1 v& a klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
' V& F" R) I/ v6 c4 O' d0 L. f}* \5 [+ A n+ x) j
其实,所谓的绑定,就是将设备的驱动节点添加到驱动支持的设备链表中。
* A. @8 `- c# z! U' q8 N3 Y* `至此,通过内核链表,这个platform device 和platform driver 已经绑定完成,将继续遍历内核链表尝试匹配和绑定,直到链表结束。- B# C* Y* \! a% H5 C1 z. [
1 B1 e5 E- B/ N- C在driver_attach执行完毕以后,bus_add_driver函数还有些剩余工作要完成。
9 w4 d& T4 D7 i( r e1 p9 q+ L( r0 M
8 j- m% [/ [) C$ f% F3 d4 w9 M首先,将驱动添加到总线的驱动列表中。
" W- m6 R& k' J9 |' n$ m! D接着,如果定义了驱动属性文件,则创建。
% `0 W. ?+ @1 X! e最后,在/sys/bus/platform/drivers/s3c2410-spi/下建立属性文件uevent,并在同一目录下建立文件bind和unbind。$ m4 D2 U! q7 a* K) x
5 m r$ {0 u" _1 l
我们来验证下:
. f- j. s) ^1 @& m4 |' T8 I9 K, h, ]$ B
[root@yj423 s3c2410-spi]#pwd
" u) @, V: `: a- N" H/sys/bus/platform/drivers/s3c2410-spi
( G, Y! \) U1 C; o3 a2 D" ~[root@yj423 s3c2410-spi]#ls
7 L9 a7 b( { Q+ Qbind s3c2410-spi.0 uevent unbind
- R3 z0 Z5 U& E4 q( J+ ], e( ^3 |7.7 小结( ^" x8 d) L- P1 U5 D* _
在本节中,我们看到了platform driver是如何注册到内核中,在注册过程中,通过更新了sysfs,向用户空间展示总线,设备和驱动之间的关系。3 \+ B# P4 _7 e8 V4 A
" V) h5 Z0 ~) i* S同时,还更新了链表的指向,在内核中体现了同样的关系。" F4 f( |6 k$ O& v' R& J! v
, L4 M( I3 W9 S) c0 R9 [3 a+ Z; G/ j
最后以platform driver的注册过程结束本章。
; W, D# w3 ]# n, S6 y; M; q# F! D9 `, B# f9 ^
% [& D8 c9 c/ o- {6 w9 G% a3 a. H" n3 X+ h6 C* Z
8. sysfs底层函数! d: o) ~; u% Y+ G$ c7 ]
下面讲述的内容将基于VFS,有关VFS的基本内容超过本文的范围,请参考<<深入理解Linux内核>>一书的第12章。
3 {& Z& B9 h, w在前面讲述的过程中,我们知道设备驱动模型是如何通过kobject将总线,设备和驱动间的层次关系在用户空间呈现出来的。事实上,就是通过目录,文件和symlink来呈现相互之间的关系。在前面的叙述中,我们并没有对目录,文件和symlink的创建进行 讲解,本章就对这些底层函数进行讲解。在讲解这些函数之前,我们先来看下,sysfs文件系统是如何注册的。
+ b. B6 w$ @) z0 O5 j$ z, I; q& ^7 ~; F9 ?
0 ?0 N# j* J8 P9 b7 C9 a8.1 注册sysfs文件系统) ^2 r. |) O7 @2 F" Q$ @
sysfs文件系统的注册是调用sysfs_init函数来完成的,该函数在内核启动阶段被调用,我们来看下大致函数调用流程,这里不作分析。0 W, i5 [, g; G, T" ]+ q
start_kernel( ) -> vfs_caches_init( ) -> mnt_init( ) -> mnt_init( ) -> sysfs_init( )。
4 U) N0 a7 f2 r1 \/ s- m
8 y7 K- x) l9 I5 rint __init sysfs_init(void). ~4 C7 g1 i; `% y7 j0 q
{
# u' G j5 H& ]8 o& ~$ ~. J" W int err = -ENOMEM;0 h0 L9 f. e& T% ? }
/*建立cache,名字为sysfs_dir_cache*/
/ C8 f7 y3 M4 G! P, C sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", l3 F; V# F8 ^0 s* v
sizeof(struct sysfs_dirent),
% r( Y" G* O& w8 P0 U2 H: L1 p 0, 0, NULL);
# g8 q) v' ]% @# J1 y if (!sysfs_dir_cachep)+ c. }: M% J, |9 t
goto out;( u) ?, D* `1 L0 T. V) G
) B. Q) Y% u- d# [
err = sysfs_inode_init();
3 ?+ c0 W2 F7 p' ?7 z; M# z/ _5 m! i if (err)
/ A. t/ S* r$ `( u5 z goto out_err;
& ?* X6 Q \; d+ v( ^* N /*注册文件系统*/) g8 x% b7 K5 S4 c5 [0 ?2 [' z
err = register_filesystem(&sysfs_fs_type);
+ {4 [5 }) S, }; p if (!err) {% b/ I( w/ S) u$ o1 O# U! c
/*注册成功,加载文件系统*/1 N; p( c6 }$ H/ `' M
sysfs_mount = kern_mount(&sysfs_fs_type);9 G! s, c. s* u% V# F- _9 A5 n
if (IS_ERR(sysfs_mount)) {& X$ B5 m6 y f3 w7 s" ?
printk(KERN_ERR "sysfs: could not mount!\n");
& S5 w1 Q1 r1 P) M6 s err = PTR_ERR(sysfs_mount);9 x, }5 r& Y! G: A0 c# \8 I. N. T) @1 z
sysfs_mount = NULL;: P% {* n* L/ L- w
unregister_filesystem(&sysfs_fs_type);
0 D4 m; u5 r ]( H- ~ goto out_err;
0 w; C* j, i. A7 H. ]8 ~ }
9 g% _, x) o5 C0 E } else
5 p! |" F! x* |" l5 r goto out_err;" L8 g' d- `7 _( A( [2 c9 Y0 C8 G
out:
& e- B: c! z; C5 M return err;
2 U( Y; M' H9 y3 e. Q9 Sout_err:% y% t) ~# h# k: G
kmem_cache_destroy(sysfs_dir_cachep);
# |3 `! r9 |( Q: X: h sysfs_dir_cachep = NULL;
) G, G( H4 g$ I7 b2 ? k goto out;
9 {% k& }( C; v q" f}; O6 Q7 @1 Z4 ~3 M9 K
' E" D4 S5 u5 z7 t6 y0 B& ~. X: I" |' P
static struct file_system_type sysfs_fs_type = {
' ^5 V* N1 F: D .name = "sysfs",0 v" B* u' b# }
.get_sb = sysfs_get_sb,
b2 r/ o- c/ b3 h4 u+ P% O9 Y4 q .kill_sb = kill_anon_super," w' F" j4 z9 X4 L1 I& l
};
/ M+ S) z a; V+ n- h/ |
0 n$ V7 b! K) A [$ A8.1.1 register_filesystem8 E. S" d$ m" L; d3 a5 _
下列代码位于fs/filesystems.c。
# h: j }6 A, C2 h/**
! W+ l8 F6 F v4 `4 Z! N3 x * register_filesystem - register a new filesystem
( k3 c. D8 u2 @8 u * @fs: the file system structure3 \6 y! G/ z" G
*0 d! R3 K- B6 W. r+ e& b
* Adds the file system passed to the list of file systems the kernel
% h4 I! B5 B' ~& B9 w8 k+ `9 R * is aware of for mount and other syscalls. Returns 0 on success,; O. V! _( N G/ Y4 \# P: O
* or a negative errno code on an error.: A) c$ ]7 k% c0 O9 u A/ m
*
* h9 W9 ?- \% M, F& T$ ] * The &struct file_system_type that is passed is linked into the kernel
2 y) C% a& X9 z9 b3 E * structures and must not be freed until the file system has been
- K* k- k4 S- N' |3 `' K * unregistered.0 S5 T- l7 B" [% Q9 L4 r# H0 P
*/
" c2 }7 s* l% r
2 V3 [! }' R7 mint register_filesystem(struct file_system_type * fs)) f' ]8 z2 A3 w% j1 \
{4 r: |; R; f& d! N) ?8 t
int res = 0; u4 R \- V( ]* z
struct file_system_type ** p;! ?) ]. s5 d$ _& q( L
# [7 j7 X4 _5 c1 ~# | BUG_ON(strchr(fs->name, '.'));0 f, a: K; ]- q8 G$ R/ b
if (fs->next)! L8 O0 f" k* _! C3 h& N
return -EBUSY;
* o; z. a7 v4 I h7 n+ r/ i8 I) [3 I INIT_LIST_HEAD(&fs->fs_supers);
5 d9 h8 ?; q. v write_lock(&file_systems_lock);6 Z/ M2 x5 C3 O9 K. J' Y
p = find_filesystem(fs->name, strlen(fs->name)); /*查找要住的文件是同是否存在,返回位置*/% s0 O, I V; ^
if (*p)
7 ^( a5 G8 t6 ^8 m; D. g' ` res = -EBUSY; /*该文件系统已存在,返回error*/
; f: s/ A4 {: O else
! b3 Q }+ d2 z3 ]8 ^ *p = fs; /*将新的文件系统加入到链表中*/1 P0 t( z$ T$ o0 W. R) h6 b, s
write_unlock(&file_systems_lock);
6 V! c9 r5 `. ~; v4 t return res;
/ }" d4 j3 g$ e' [}6 z) }1 K$ Z% V3 A( Z! m3 x
static struct file_system_type **find_filesystem(const char *name, unsigned len)
J) ~* s, {+ P7 S9 ?! ^7 }' C% [{0 \" n, Y, m4 `$ O
struct file_system_type **p;
4 H2 n9 ^* M$ _. s; J& b/ S7 O for (p=&file_systems; *p; p=&(*p)->next)
9 ^* L" L! X' u' j6 A if (strlen((*p)->name) == len &&
1 N% c- B7 v0 r& e. U" Y0 D) l7 w7 ~ strncmp((*p)->name, name, len) == 0)7 z; [- E- _, h- P" @0 f
break;
9 Q% t D. w# e1 ^ return p;
. |8 J$ p, q c1 n, a1 F# x}
+ {: d& e) g4 v! ]* t' M该函数将调用函数file_system_type,此函数根据name字段(sysfs)来查找要注册的文件系统是否已经存在。
& \% B2 E2 B! o) K8 S2 ^如果不存在,表示还未注册,则将新的fs添加到链表中,链表的第一项为全局变量file_systems。 D# c7 k0 \+ g+ I8 p/ y7 R
; S/ X$ z2 R' U! W/ L5 A
该全局变量为单项链表,所有已注册的文件系统都被插入到这个链表当中。
& a' u* W! \; Z& v: M6 c' C% V4 h$ ^9 G7 M
8.1.2 kern_mount函数2 `8 u2 [3 W, }1 B/ \
下列代码位于include/linux/fs.h
0 S7 o# k4 c3 ^: g: g
. B8 o4 K1 H! p#define kern_mount(type) kern_mount_data(type, NULL)
" f# ?) Q6 O# Z H. n+ w! r/ f6 a+ h下列代码位于fs/sysfs/mount.c
( Z' ]+ Q/ b' F% t* P8 }1 _struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)7 b- C( W) q, X& y X3 h+ N
{
, A+ ?2 G7 q' L% P8 g5 Q9 A return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);7 j( h% D2 O0 |; h! z6 V- a
}
, @1 d$ e0 O" }4 p( O3 H4 P3 f5 Y& ?, j M- { L6 z, C4 |
EXPORT_SYMBOL_GPL(kern_mount_data);) R1 E0 N3 x0 K
kern_mount实际上最后是调用了vfs_kern_mount函数。我们来看下:) Z7 i' E: ]8 o; m$ \$ t' c9 v
7 X# _9 j( s) l j
struct vfsmount *
1 F/ s7 g& ^1 L, d% w- n; l7 kvfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
6 v$ }7 V7 t2 A3 B& p+ \{
" q: Y' z. s$ S% U Z8 M8 ` struct vfsmount *mnt;
- o- ^! A4 J- p4 o) I' }5 \ char *secdata = NULL;
. J. I, E' i; p/ B int error;. k" j* o3 ?" T8 e; i" h7 c$ j8 |9 v
9 b4 G' K+ \' `* _ if (!type)
' U- s; P; o+ q$ R( Y) h! E2 x return ERR_PTR(-ENODEV);' e; h' T1 n- ?4 v: L/ Z9 Y$ q
% h+ x( U- Y L8 P. V9 u
error = -ENOMEM;" V: C0 S! d R# |
mnt = alloc_vfsmnt(name); /*分配struct vfsmount*/+ b+ h' s$ o7 Q9 h
if (!mnt)' F; s/ T) G% ^+ X4 J# j7 E: ~* Q
goto out;8 R, Z& h. Z( d. T" X( L
) I. u$ ]) M' l2 ~4 Q if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {- Q1 C7 O, ^! G. _; [, e* U
secdata = alloc_secdata();) g5 a; M0 b$ X& F, ]# l2 i
if (!secdata)
$ Q8 i, i8 Y, X! H1 g goto out_mnt;, q% {, U8 K9 y C! Y8 Z
* L% B! @$ V9 G& Y3 d. t$ N error = security_sb_copy_data(data, secdata);
. V2 \5 _9 Q- a0 i" R if (error)( X+ O3 v/ R! A# Z
goto out_free_secdata;
( B' P$ B$ ?) t4 v X# m }$ B8 Y2 G8 ^( l2 ~# o# y7 J" Q
/*get_sb方法,分配superblock对象,并初始化*/
' M4 G6 o6 ~" f7 U2 o1 `3 f7 n0 M s error = type->get_sb(type, flags, name, data, mnt);
! S$ z/ z8 Y% }& s if (error < 0)3 f* Z% F9 ?. d" I' ?2 }/ |5 V* v0 {
goto out_free_secdata;
5 F) }, _6 y' F# s! u7 P3 L* }7 ~, e2 S% ] BUG_ON(!mnt->mnt_sb);: |$ L$ [: C- ^9 r: ~8 {
' N5 c! Y7 j. L- o error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
$ L$ [3 W1 |* u5 n# R, | if (error)1 P. A5 i" s/ L1 M2 x" `
goto out_sb;. L) D2 E+ o" o1 }% j3 d+ \
( p9 s0 J2 V+ A5 g: L8 U
mnt->mnt_mountpoint = mnt->mnt_root;/*设置挂载点的dentry*/( K3 u! w+ f0 x1 y- J1 E n+ N
mnt->mnt_parent = mnt; /*设置所挂载的fs为自己本身*/
: K# k7 F6 K. |. l/ c3 ?4 X+ ~: W9 X! g up_write(&mnt->mnt_sb->s_umount);
. I) j6 e& F. o4 @6 v1 w free_secdata(secdata);
; ^" C. I4 v. B$ ^1 z* Y, `8 Y return mnt;$ [! ]5 M1 U3 |$ x1 `) J% h
out_sb:8 i" ]1 ?5 S% f. |& e; z
dput(mnt->mnt_root);
) P$ Z# {' H0 p deactivate_locked_super(mnt->mnt_sb);. N B. X' O. d8 a5 n, A- u: ~
out_free_secdata:
! ~ _7 L2 `1 l; E' U3 Z0 ? free_secdata(secdata);
( m$ v- C$ N/ A# `8 yout_mnt:
" f( x0 K, t. b8 d free_vfsmnt(mnt);: B9 r9 M( V K& G0 A- ?4 X; x
out:# L/ `- Y$ D. v* l4 g
return ERR_PTR(error);6 w. { `- E2 ]
}
( J& Q0 O8 r; {3 _8 K% |% j1 f# ~3 e+ t" `
该函数在首先调用alloc_vfsmnt来分配struct vfsmount结构,并做了一些初试化工作。! }2 x9 v Y7 @4 A! B! _- }) Y( _( ^
下列函数位于fs/super.c
( w3 _+ F7 j2 H. l+ dstruct vfsmount *alloc_vfsmnt(const char *name)9 x1 y9 A- b' z# G1 ?' A# J
{
. |2 E3 S+ m! b/ E2 D struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);( Y4 [" H R: q: ^. \
if (mnt) {
; @+ R+ ^4 [9 v) I int err;
- ^, `" w& O O9 r
7 ^, N1 P8 H3 h err = mnt_alloc_id(mnt); /*设置mnt->mnt_id*/; E! T6 X" E8 ?, Z
if (err)
2 e' ^8 O% y* u! e/ q# { goto out_free_cache;6 B/ I1 w7 Y$ M; [0 [
0 S- x" N( h* N3 A/ C
if (name) {
* X& a. A$ L/ [) t& A. b$ ?. r mnt->mnt_devname = kstrdup(name, GFP_KERNEL); /*拷贝name,并赋值*/
% R* `' g* m8 ~7 r3 L4 {! ~6 B( c" ` if (!mnt->mnt_devname)3 H8 `1 U, [8 q% H* L
goto out_free_id;" C5 f6 v( k5 V9 \+ E4 z' K2 \# D
}
/ F3 a7 p! o1 b$ K' p: `9 _& _
7 }. {2 x# j- P* n' N) u& O8 x9 I atomic_set(&mnt->mnt_count, 1);
% I8 M' C+ |4 ]- [% L INIT_LIST_HEAD(&mnt->mnt_hash);
$ c" j a" C4 j: G% v) n INIT_LIST_HEAD(&mnt->mnt_child);
C) k |1 B' s. \: E/ d7 o INIT_LIST_HEAD(&mnt->mnt_mounts);
. q' _6 N8 p" B4 S! O) P INIT_LIST_HEAD(&mnt->mnt_list);0 x g2 m f7 Z
INIT_LIST_HEAD(&mnt->mnt_expire);* B& h- Z% |# Q* I! @
INIT_LIST_HEAD(&mnt->mnt_share);* M* C9 U4 n$ S4 l; j
INIT_LIST_HEAD(&mnt->mnt_slave_list);
& g) u/ c8 g" u7 w( {, \9 x( _1 @ INIT_LIST_HEAD(&mnt->mnt_slave);) p% c+ n0 x2 C/ N9 ~9 }! R6 H
atomic_set(&mnt->__mnt_writers, 0);! f/ ~; R8 f$ O" H3 J
}
# x' j1 V2 l0 C1 z% b return mnt;& O3 ~# y- _9 b
7 W p9 k5 k7 o$ v* K; sout_free_id:
: Z T# f+ V, I, L* u1 G- m mnt_free_id(mnt);
) `4 Z3 M( Q+ x* `out_free_cache:
p+ z% \0 E6 O; } kmem_cache_free(mnt_cache, mnt);
* S5 _$ z6 @7 H return NULL;
7 N, d. W+ a3 q$ V" i( d( Y" L}% b) s- i) E5 h3 w3 H/ Z8 Y" X
分配好结构体以后,由于参数data为NULL,将直接调用文件系统类型提供的get_sb方法,该方法就是函数sysfs_get_sb。我们来看下:& [5 ]7 Y' j8 ?2 G8 i/ {9 r, E
下列函数位于fs/sysfs/mount.c。
- w1 s: R( _1 q: i8 K( zstatic int sysfs_get_sb(struct file_system_type *fs_type,
8 g' A I( P( y int flags, const char *dev_name, void *data, struct vfsmount *mnt)0 N l& i4 N( X; ` e y1 K4 z a. i, e
{
' c# f$ D4 ~4 q% E4 P! J- Z3 F return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);- A5 t0 G& \8 H4 O
}/ s* @7 Q0 @! k& [( z+ ]) |
这里直接调用了get_sb_single函数,注意这里的第4个实参sysfs_fill_super,该参数是函数名,后面将会调用该函数。
+ m7 K5 s* y5 H6 H; `: Y3 r该函数将分配sysfs文件系统的superblock,获取文件系统根目录的inode和dentry。0 @; w0 R+ E5 E+ \& F
' J% l' |+ ~+ L4 z- j" \
该函数的执行过程相当复杂,在下一节单独讲述。/ e, E, m" X" L
9 U0 D( o- u8 y' F( D5 ?$ ]
8.2 get_sb_single函数
( V9 c! r$ D* L下列函数位于fs/sysfs/mount.c。
) k7 E' O( P4 P$ E1 Y* G
0 M# y" N% J5 V8 ~, ~4 k% Yint get_sb_single(struct file_system_type *fs_type,
3 u( e' t5 j1 E( w. C3 m int flags, void *data,
?$ S: w7 T& L4 ]# b; y6 R int (*fill_super)(struct super_block *, void *, int),
9 R! n- T' q# D" _5 D$ h8 _ struct vfsmount *mnt)9 o: s& P5 m% [/ \( a& o
{
0 ~+ I) m: @! m* u( R struct super_block *s;
0 v9 }3 r7 v6 C1 @( s. l' ] int error;
& H5 a% J5 I6 @( A* N/ _ /*查找或者创建super_block*/
8 @$ g) x2 @5 L* b# U# c7 y s = sget(fs_type, compare_single, set_anon_super, NULL);
z0 Q6 z# l: l: i, | if (IS_ERR(s))
' P1 t$ L0 |; ]) h# X6 X i return PTR_ERR(s);' S& ]' J& k* }# ^! V2 R
if (!s->s_root) { /*没有根目录dentry*/
) d) \ t" n0 ]2 Z+ o+ J s->s_flags = flags;
- X( j2 ]; w8 C8 o6 T1 a# ? /*获取root( / )的 inode和dentry*/
5 V: Z5 K/ w* J error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
2 {; f2 x m% N( C6 _, T if (error) {! m# f- ^5 n& c. \
deactivate_locked_super(s);% T3 y8 H/ }5 n( r3 x% h
return error;
2 n, l3 \; G1 A/ O }: c( E5 J4 y- K5 {* D
s->s_flags |= MS_ACTIVE;9 p5 m# k! j9 R) I$ v
}
/ Y+ a# N1 ?2 J1 K do_remount_sb(s, flags, data, 0);5 n/ w4 @, m) k- J3 o4 F2 v
simple_set_mnt(mnt, s); /*设置vfsmount的superblock和根dentry*/
, X# N, o0 L( |/ x# `% b. z% t return 0;
4 S9 _3 v# d# b1 H}2 ?" f4 R) b- e& l% n5 u5 y
+ R3 o U V& IEXPORT_SYMBOL(get_sb_single);: k4 i# p9 R R: x0 c! h, r6 U( J U
8.2.1 sget函数
) s# p1 @/ d( d3 ]6 j7 s" }首先调用了sget函数来查找是否# C2 ~4 Q8 M# o3 o: Q( I
下列函数位于fs/super.c。& k- C4 J9 V) U. p7 K o5 ]
/**! d+ a9 J! P F
* sget - find or create a superblock
% P' b/ m+ n: W9 [ * @type: filesystem type superblock should belong to
r$ O" ]3 w0 F+ o6 o; w+ A, ^4 _ * @test: comparison callback
+ T: M5 w: T, {& Z * @set: setup callback
; O( Z* J0 ^* M) g# f8 T9 |" | * @data: argument to each of them
; c+ @' Y3 ~6 D5 d */
+ p* J5 X4 u& dstruct super_block *sget(struct file_system_type *type,
* b& a: z1 a' D! T: ^ v int (*test)(struct super_block *,void *),
2 n. _; b/ g0 O6 A. z# O; M3 S int (*set)(struct super_block *,void *),, _1 v; ]/ b: U5 a
void *data)1 ^1 C) c9 E# ?3 r0 A
{
, q2 {. M/ s% J2 z struct super_block *s = NULL;
' T! g6 T) E9 u4 P U# ` struct super_block *old;9 u& ~; {* b! I# D* O7 ^
int err;1 h! I. f% ^1 p8 O# j4 y2 B- g
M7 H8 ?# R& K/ `3 h5 ]6 t @retry:3 x$ l, G* j1 } J, O0 _1 Y
spin_lock(&sb_lock);
( o& s& |7 `8 y% A6 |+ X" `9 n if (test) {
) A4 P: H. `. } T8 n /*遍历所有属于该文件系统的super_block*/
! S, B" m* R- p9 P) Q! f list_for_each_entry(old, &type->fs_supers, s_instances) {
( v+ s# V! I9 d& e7 F7 p if (!test(old, data))
' f6 \# |# y0 B; E2 m$ ~ continue;0 M$ a/ P% _+ x4 @7 [
if (!grab_super(old))
3 ^3 ^6 S! r( B goto retry;
E' j3 M* Z& } if (s) {9 J' t% f. @# i- n' F
up_write(&s->s_umount);3 a# _: q) |, y
destroy_super(s);
9 T( h# s* p9 ~8 ]: s; B }
( C1 i9 t; v! Q' d return old;7 x2 J2 O& W9 y" G& z% M3 o
}
- B* l( {7 X# ]% W5 T: S }
8 p+ A% X' ~' T& [3 [ if (!s) {
5 n- G+ _" m6 d6 D9 c spin_unlock(&sb_lock);
. v6 ]1 j) Y1 u y0 ^ s = alloc_super(type); /*创建新的super_block并初始化*/
) y5 K0 M3 H% S! m7 Q" d$ F# a if (!s)
- j$ M3 Z+ d6 h n0 S return ERR_PTR(-ENOMEM);. F; D; J. r8 A; `1 ?+ p8 {
goto retry;! u5 m: |9 g$ {, r
}1 P" J2 }& s! Z$ i( l
0 u' `2 X# z* O2 j! u err = set(s, data); /*设置s->s_dev */4 c9 Y, [9 @& \7 \* S
if (err) {% }. t" d4 E% `* w
spin_unlock(&sb_lock);
) y' {+ K/ D- Q3 e6 |9 o1 q up_write(&s->s_umount);
9 U- ^! @% J4 z! U2 m( A' n destroy_super(s);" `# [( [* Y2 ?% z, r& ?: W
return ERR_PTR(err);
, _- H: ~7 s3 q. g9 Z; } } |5 M7 Z8 w0 I* W) i5 M' [
s->s_type = type;
# Z7 x3 X- U; X! ]2 U/ B4 e strlcpy(s->s_id, type->name, sizeof(s->s_id)); /*拷贝name*/
5 O$ Z, b1 p2 W7 B& A! c list_add_tail(&s->s_list, &super_blocks); /*将新的super_block添加到链表头super_blocks中*/' N7 u8 `, b) |3 t! \9 S" c$ H
list_add(&s->s_instances, &type->fs_supers); /*将新的super_block添加到相应的文件系统类型的链表中*/7 p3 \, W) E7 s! d
spin_unlock(&sb_lock);: ?" ?, B/ w8 G/ ^3 {# K$ I
get_filesystem(type);
7 L) a5 v4 h. p6 u/ m5 c1 q$ G return s;
* n6 K% H5 l+ w" [5 j' X; A/ H}9 d9 [# F% \2 \1 z
' I! C5 p( P% C) i
EXPORT_SYMBOL(sget);
0 O: |# ?: J& s该函数将遍历属于sysfs文件系统的所有superblock,本例中由于之前没有任何superblock创建,遍历立即结束。
$ e7 w* E+ f2 [: v5 S然后调用alloc_super函数来创建新的struct super_block。
- L% X2 \* i1 }! v
8 r( e4 w. o @: t: n下列函数位于fs/super.c。: p& E3 ^0 }- F% v0 F- N
/**
/ b2 r1 R) l# G* m) B1 W * alloc_super - create new superblock1 j" t: p- {0 b
* @type: filesystem type superblock should belong to
( b( u) p& d$ Y8 f9 ?# \ *
! ?1 v( p2 x8 I5 V# f * Allocates and initializes a new &struct super_block. alloc_super()' O: a# m: L6 H d
* returns a pointer new superblock or %NULL if allocation had failed.
- R; q6 q3 y/ b( k9 M7 e' M */* H, d0 t5 {" s9 r& r4 H* X
static struct super_block *alloc_super(struct file_system_type *type); J9 |! F8 g' W0 ?/ M" C) k
{7 F/ v, K7 z3 I# Q. {( H0 n
struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);/*分配并清0super_block*/
7 b- I( e1 j V: e. D; h9 b static struct super_operations default_op;# J" D" _1 ^( c# x
+ F0 H1 L4 q1 A. n$ w# h$ n7 G if (s) {& p8 ^( {& u9 _$ m% |! r
if (security_sb_alloc(s)) {8 k: o# J: l* n# W8 t8 y
kfree(s);
" U. L- l, E( H* ? s = NULL;/ P' z9 J" ]8 u8 f0 `
goto out;( A2 A4 M$ a; z
}# E/ u4 ]7 i: D: m* d/ t
INIT_LIST_HEAD(&s->s_dirty); \! `( g8 r4 n' a( H0 c4 n1 ^
INIT_LIST_HEAD(&s->s_io);
% z" p9 _% @+ k# q8 R5 C! a6 M INIT_LIST_HEAD(&s->s_more_io);
% M4 Y$ l8 X. o% V8 O INIT_LIST_HEAD(&s->s_files);3 q+ N% Y, B! a) }0 S
INIT_LIST_HEAD(&s->s_instances);
~4 A+ k" h: [6 ~) m. } INIT_HLIST_HEAD(&s->s_anon);
+ L, Z( \2 y U7 J- l0 r0 U) F INIT_LIST_HEAD(&s->s_inodes);
# h1 O9 @& D* E INIT_LIST_HEAD(&s->s_dentry_lru);/ t/ }6 U& a9 A. h' f
INIT_LIST_HEAD(&s->s_async_list);
$ H- S+ C0 P& L5 X8 | init_rwsem(&s->s_umount);
0 g- R5 T0 u. B% u5 E- d) N! k+ i8 j mutex_init(&s->s_lock);6 m" S w/ V: `% [$ w) U, [2 b
lockdep_set_class(&s->s_umount, &type->s_umount_key);- O! o/ Z4 ]( n; m( d3 R- P
/*% O3 P2 F. l% |' j0 E. f, h
* The locking rules for s_lock are up to the7 P- Q5 \- ?, j* N6 l8 u
* filesystem. For example ext3fs has different
5 ^# A' j& c: s6 A0 {9 c$ t* Z * lock ordering than usbfs:
% Y" G4 t2 g8 g3 o */$ j; O, V5 e* R, F1 l8 d
lockdep_set_class(&s->s_lock, &type->s_lock_key);
; W' i7 S; G1 S9 F! T /*
# Q" J- d4 K' h/ [! d1 @ * sget() can have s_umount recursion.2 F: ^9 ?+ ~9 \7 C. T8 z/ i: J, t
*- I( N( u" k9 ~9 B* ~/ A ]
* When it cannot find a suitable sb, it allocates a new
; P+ N( d; |. Z/ n3 D) ] * one (this one), and tries again to find a suitable old
9 M- Z" A- Q$ I# ^ * one.2 }6 o4 u5 P) u5 n6 A9 g
*
7 F2 L0 z- Q6 A * In case that succeeds, it will acquire the s_umount7 l( w" D5 d4 L3 c; |" A
* lock of the old one. Since these are clearly distrinct1 s; {- m: _0 Z/ G& \- `
* locks, and this object isn't exposed yet, there's no# q& ?4 X) W/ C" z/ u* C9 F( t" R/ k: p
* risk of deadlocks.. ]3 y; M* i2 d- L3 m. n1 f: @: [ r. ~
*' } V2 X1 t7 T3 k' G
* Annotate this by putting this lock in a different
" m) }" O! t; F& @& t * subclass.
; G' l/ v A) O4 R */
5 b- i$ c$ G5 ]+ [8 G down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING);9 J' h) ~' T" v4 t, {5 j; @
s->s_count = S_BIAS;$ v. N. y9 {, c% Q
atomic_set(&s->s_active, 1);. n& n+ t( V% O2 j1 Z
mutex_init(&s->s_vfs_rename_mutex);7 W- t5 x: |- g# [; g ?! O. u% {
mutex_init(&s->s_dquot.dqio_mutex);
F' q; n$ A* M1 U4 | mutex_init(&s->s_dquot.dqonoff_mutex);
; j! X) V& |. i8 X2 W init_rwsem(&s->s_dquot.dqptr_sem);
+ \: C. R7 P" U3 N% h$ r% P init_waitqueue_head(&s->s_wait_unfrozen);
- ~* t9 N+ m( V0 `8 U$ ?# t2 x s->s_maxbytes = MAX_NON_LFS;' n0 i' h0 A! X. x( N
s->dq_op = sb_dquot_ops;
% J! W1 L: O! i' S+ @) h9 t. h s->s_qcop = sb_quotactl_ops;1 h( Y: g: r$ E2 [, V; D
s->s_op = &default_op;
) O' h( ~" \" V/ m s->s_time_gran = 1000000000;( O0 U' W6 {+ N e* m3 t
}
* i5 f1 O- c- F. i2 a \% i/ cout:
% K! h& n6 X5 s/ q return s;
' [' ^8 ~+ M' ?4 b9 N}
, H" N" k7 ~! x9 f& `分配完以后,调用作为参数传入的函数指针set,也就是set_anon_super函数,该函数用来设置s->s_dev。" n ]$ p1 Q% w. K
下列函数位于fs/super.c。5 ^% Z2 t+ l5 ]# h, W/ ?2 ^* H
int set_anon_super(struct super_block *s, void *data)
6 W" z- N0 h1 J r1 }{+ O+ f# L2 F3 O+ N X
int dev;
2 U" V( x( ~, y9 }0 S+ s {# o int error;
# d. D5 i# }* F+ B1 H' c
- F- {, x; l P% h {$ G retry:
" @; W) n+ E b- Y if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0)/*分配ID号*/& J% e$ V- E+ K$ r$ N4 i/ l
return -ENOMEM;
# @* v$ r% V: q! A0 v8 }& w: q spin_lock(&unnamed_dev_lock);& ^! w q( s4 |+ p; ]8 F; u* M6 @" c
error = ida_get_new(&unnamed_dev_ida, &dev);/*获取ID号,保存在dev中*/
! C( W' v& f3 k1 C spin_unlock(&unnamed_dev_lock);. r ^. w# b& {: _& s- V
if (error == -EAGAIN)
( @+ m- q' W# M3 W% P /* We raced and lost with another CPU. */3 M" y `* `& V/ x
goto retry;
1 m0 }6 z0 T0 ^8 R2 l3 r: H; U else if (error)
+ e2 r; S& L) z1 M6 u return -EAGAIN;& z! ]: w' v7 j0 k8 ?1 z+ X
8 x( r3 L# [# @& k2 a4 n
if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {0 F- o% H5 k' t
spin_lock(&unnamed_dev_lock);
$ {1 |# R3 H! l" B& D ida_remove(&unnamed_dev_ida, dev);& a8 A% S b O+ I
spin_unlock(&unnamed_dev_lock);
) _; P$ N' U' U# a! E return -EMFILE;: @9 o4 Z. d- @& s+ c9 Y) |
}
+ i% B+ ?6 S: N6 V" P6 u6 n& S: w s->s_dev = MKDEV(0, dev & MINORMASK); /*构建设备号*/
* y- k8 i+ K' G8 S( ] return 0;3 \4 X. l/ E# M) `) h) ^0 K0 |+ W
}! a$ N- |- t# a3 k3 B
8.2.2 sysfs_fill_super函数% w! K5 S2 h( p2 z V6 q# ^# O8 L" i
分配了super_block之后,将判断该super_block是否有root dentry。本例中,显然没有。然后调用形参fill_super指向的函数,也就是sysfs_fill_super函数。
; G2 y* C R1 ^) c9 \, u' W3 K6 O; g0 o; f. W! T
下列函数位于fs/sysfs/mount.c。
; C s/ I1 t* D) N% {struct super_block * sysfs_sb = NULL;) f) b% F Q* t/ r/ Y
: o. w+ T5 I8 \- h* G) S
static int sysfs_fill_super(struct super_block *sb, void *data, int silent)2 z1 ~5 E; v2 h2 n: F
{
, W t- q5 A* Z7 @ struct inode *inode;
+ M$ E: ^) V7 Q7 H- e8 b, b struct dentry *root;7 y$ \" K$ ] O- w
% b& @5 |# O2 i& X$ O5 N. d
sb->s_blocksize = PAGE_CACHE_SIZE; /*4KB*/# F4 Y+ E% k/ d7 Y0 F% _
sb->s_blocksize_bits = PAGE_CACHE_SHIFT; /*4KB*/
' F% E' R$ h7 K$ T sb->s_magic = SYSFS_MAGIC; /*0x62656572*/
! b1 K! M+ h; T }7 O3 M5 d) B5 z B! y sb->s_op = &sysfs_ops;
& ~( W: k9 `7 p3 U: N' ^8 Y$ X sb->s_time_gran = 1;# o; x" z8 P2 d4 j
sysfs_sb = sb; /*sysfs_sb即为sysfs的super_block*/
5 ]2 r- |* h4 b4 Y4 ?# N5 s" X7 j& q% |7 ~4 s3 L
/* get root inode, initialize and unlock it */( X! e7 b; t1 ]# l
mutex_lock(&sysfs_mutex);
5 v8 b$ {1 i+ U inode = sysfs_get_inode(&sysfs_root); /*sysfs_root即为sysfs所在的根目录的dirent,,获取inode*/
/ `" |* C4 r$ r mutex_unlock(&sysfs_mutex);
& J8 S( \6 N: M! V if (!inode) {
# I; p) ~/ D$ K2 \7 I( @1 l0 T pr_debug("sysfs: could not get root inode\n");
$ K. R5 `# e7 ~+ `* V, m return -ENOMEM;, u$ r, h' G+ V( y, s+ {
}
9 n1 y3 ^- e; ^# s0 M1 m5 h9 p( F3 }1 Q. X$ b
/* instantiate and link root dentry */
! H5 e R, Q% O root = d_alloc_root(inode); /*为获得的根inode分配root(/) dentry*/
I3 {4 t; J! r8 L5 r9 I if (!root) {( ~3 x+ o9 e4 v( S+ p# z: ]
pr_debug("%s: could not get root dentry!\n",__func__);
0 N9 X9 ], `5 Q4 v+ C7 m0 J iput(inode);. g& m% y! i0 N) D
return -ENOMEM;
5 I! A& y* I. H3 l }
; r. n' T/ J0 D- W. H$ I: Y4 _ root->d_fsdata = &sysfs_root;( r* e* R2 `$ j; C0 [2 x: ~: B
sb->s_root = root; /*保存superblock的根dentry*/
1 d a# k6 M( c. U4 T( O0 C return 0;7 ~( m3 J2 g3 h* O) u! G
}) W V! A( {$ j9 t
9 L) l5 ?0 r$ {: B8 U* M+ v& y
struct sysfs_dirent sysfs_root = { /*sysfs_root即为sysfs所在的根目录的dirent*/) i6 E$ E1 X# z
.s_name = "",
6 {9 A' ?7 ?/ P0 a+ X% z: R .s_count = ATOMIC_INIT(1),* q" ~/ Q& h2 y* P; i
.s_flags = SYSFS_DIR,
6 a4 m) _. G: [; S! L3 K% @' { .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
$ Q% a; X8 y0 S3 u' C .s_ino = 1,
' u" P5 r+ x! H};: ~# _: f) M6 n$ s
; v, |: h; m, a6 { n在设置了一些字段后,设置了sysfs_sb这个全局变量,该全局变量表示的就是sysfs的super_block。
0 v* Z5 z3 c0 P* @4 V, b- m c( z- q随后,调用了sysfs_get_inode函数,来获取sysfs的根目录的dirent。该函数的参数sysfs_root为全局变量,表示sysfs的根目录的sysfs_dirent。$ e; }6 ^8 @2 v$ k$ B& n/ Z# v9 a7 F
3 r" | V# {& q$ {我们看些这个sysfs_dirent数据结构:" x; \0 X$ U# M2 ^# i$ [$ z' M3 d& G4 g
/*$ A- p. D+ D2 K% k7 z6 o2 `
* sysfs_dirent - the building block of sysfs hierarchy. Each and
4 U# U- u$ H! k * every sysfs node is represented by single sysfs_dirent.6 `5 Q; g, r) G% f! b* O N- P m/ Q
*
) y9 o0 g) `5 p1 T9 L8 g# r+ D$ E * As long as s_count reference is held, the sysfs_dirent itself is
- H& b; d1 A; l* i. A * accessible. Dereferencing s_elem or any other outer entity
* G4 F/ E$ p g- L- ` * requires s_active reference., V, @3 M7 v' }5 r. R' x
*/, [6 H+ _! N; U2 S) \ }7 ]- j6 T
struct sysfs_dirent {
% }) [1 y5 u6 N; G5 R atomic_t s_count;
1 j% _: Z" m4 [ atomic_t s_active;
) R# ~/ |4 s3 V struct sysfs_dirent *s_parent;
( D- _$ g/ M) z; m# z- S9 A struct sysfs_dirent *s_sibling;% [0 o& S8 [+ B/ t8 F# v
const char *s_name;8 `3 A; |3 M5 Y6 H8 t
2 V, u3 D0 q' `- {3 k: h$ B# m
union {: L3 W0 `# Q/ l% ~8 h* |" A" w
struct sysfs_elem_dir s_dir;$ P8 y" m) q& f! ?8 ?" n
struct sysfs_elem_symlink s_symlink;
: ?3 G' K; M2 i! a. ~6 C struct sysfs_elem_attr s_attr;
! J! V- H4 c' ^8 a' c# b7 E struct sysfs_elem_bin_attr s_bin_attr;
9 X% y1 ^2 L2 B( v8 U7 p6 ? S };3 T$ V# ~+ ~) r* o- F2 |
) Q. l( U8 E4 y unsigned int s_flags;7 F5 j5 `. z+ D, R
ino_t s_ino;) |4 x; C: |. W! l) u
umode_t s_mode;2 V3 S9 R: O& K6 s2 r X6 @% v
struct iattr *s_iattr;- R9 V9 t6 b) n7 |) O$ g% j
};
* {6 H5 K9 k; \3 L9 `其中比较关键的就是那个联合体,针对不同的形式(目录,symlink,属性文件和可执行文件)将使用不同的数据结构。
) P2 n$ r' a! Q# G, c! J- h另外,sysfs_dirent将最为dentry的fs专有数据被保存下来,这一点会在下面中看到。- @$ d) a, O1 g& ^; s$ s1 Y
接着,在来看下sysfs_get_inode函数:
- i' C$ c0 O- F+ N- Y下列函数位于fs/sysfs/inode.c。
. }! E J6 A5 ^1 h# N. L/**
7 Y6 D. Y! N6 V * sysfs_get_inode - get inode for sysfs_dirent2 }5 p" s$ q8 _; d+ v
* @sd: sysfs_dirent to allocate inode for7 F6 V8 I5 s7 a
*0 C4 s) J' G! m8 U8 m& `
* Get inode for @sd. If such inode doesn't exist, a new inode# C" p$ E: X) ~6 N
* is allocated and basics are initialized. New inode is0 l( J) a- q- j, u7 }# {4 h' A
* returned locked.! M# I; A0 D0 e4 Q( a
*
/ J% r# I( }% g3 ~3 M * LOCKING:' S3 c5 q1 y0 F# r. {
* Kernel thread context (may sleep).
4 R& ?: c k2 l *. [) K1 Y. |9 G$ p2 |5 z. e+ U" @
* RETURNS:5 m$ B' W# L, E2 x
* Pointer to allocated inode on success, NULL on failure.
, C/ A' E" r* J/ T1 `, X4 s. e9 z */8 M! t- l4 K; i) {3 i; b
struct inode * sysfs_get_inode(struct sysfs_dirent *sd)
6 {- J! `- L* ^7 k! Y* t, q{% y2 `5 b* E8 \. D4 P" X6 F
struct inode *inode;
$ B c# k; }9 e" z& N3 \) Q3 a0 j
' m h" H. t& B, l inode = iget_locked(sysfs_sb, sd->s_ino); /*在inode cache查找inode是否存在,不存在侧创建一个*/. G# g* K& A6 H6 q) h0 ^# U; ~
if (inode && (inode->i_state & I_NEW)) /*如果是新创建的inode,则包含I_NEW*/) s, W6 T; m5 G/ R" m4 z7 i0 _
sysfs_init_inode(sd, inode);+ K# t! f0 J5 k: i9 ^
5 `# v; k" c4 h& S/ m* e2 h3 I return inode;( Z# o; u: m! X/ ]0 f- L0 g& c
}" K+ r0 I% z4 [. {7 G, {
3 O# [6 F" |" G, F* F: [
/**- g9 F- N6 K; l1 D3 J
* iget_locked - obtain an inode from a mounted file system6 |7 Y* Z% N- }- U
* @sb: super block of file system
7 R( L( n: Z- p$ z8 a * @ino: inode number to get
* }# w1 k5 a' A* x$ q *
3 |& g* [2 v+ F% G; B$ g% A& } * iget_locked() uses ifind_fast() to search for the inode specified by @ino in
0 ?3 y; ] |8 z" t( G6 } * the inode cache and if present it is returned with an increased reference
$ {6 E. h2 W! O/ E9 E+ n& {: m * count. This is for file systems where the inode number is sufficient for
4 s, v) ?! R2 b3 ~( a: E ^ * unique identification of an inode.1 s& \7 A% i+ {5 ]
*0 w" W: @; x* G, X2 h
* If the inode is not in cache, get_new_inode_fast() is called to allocate a
+ u' e* O# J" }5 i) V3 M7 G/ b * new inode and this is returned locked, hashed, and with the I_NEW flag set.# N; [0 N) G2 e: l, L- c% Z% I
* The file system gets to fill it in before unlocking it via0 T+ K" i% o p" H- q1 @
* unlock_new_inode().5 _7 R+ ?8 O+ `/ @! h
*/' r/ l+ k+ c7 P
struct inode *iget_locked(struct super_block *sb, unsigned long ino)
! I8 r! s7 I3 F* y) N{
( h0 s* c t7 G1 I struct hlist_head *head = inode_hashtable + hash(sb, ino);
( S1 t" h( S2 W7 H6 v struct inode *inode;
7 h) `( J( }+ N( @
; B( Y' m+ m* B- r" c/ A; T% ] inode = ifind_fast(sb, head, ino);/*在inode cache查找该inode*/
9 y" |8 W5 S8 m$ C/ | if (inode)) c& Y1 }% T }) n3 T
return inode; /*找到了该inode*/
. C: x- p3 z! Q /*. F2 `9 G) {# V
* get_new_inode_fast() will do the right thing, re-trying the search, Y3 B+ `1 R3 D: M
* in case it had to block at any point.
8 s- h" b/ ~3 `# | */
/ _8 a/ K3 h" j- e return get_new_inode_fast(sb, head, ino); /*分配一个新的inode*/
" l! j! D j" I9 A6 @6 U}6 U" a: ?1 F! ?7 i
EXPORT_SYMBOL(iget_locked);) E2 b! B" [# A) i2 U- B. m
: y$ o) X' {" j% Ostatic void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)9 v2 E# X; X% Y3 Q. G
{
2 c! d$ s6 i& M$ L7 ^ struct bin_attribute *bin_attr;
4 f" f, n' j4 ?; X' A
! D: h0 j0 S( i! H- u inode->i_private = sysfs_get(sd);
. A& ~6 E7 x' {+ ~$ h7 W inode->i_mapping->a_ops = &sysfs_aops;
9 S$ L; D1 G% j7 }7 D2 E: Y inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;/ Z- u8 }7 E: {
inode->i_op = &sysfs_inode_operations;: ^3 |; x+ U( R8 d4 f) N' o9 d
inode->i_ino = sd->s_ino;# j0 ]4 y6 |, W: u
lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
. F' R; p; `* w! v$ K. M* y0 O# g) Y9 \; g1 z7 z" i% B
if (sd->s_iattr) {
; F+ C) L" V# ?" c' n /* sysfs_dirent has non-default attributes+ R* E) s" ]: p, ~
* get them for the new inode from persistent copy
+ ~9 F5 x! N7 n * in sysfs_dirent
( B/ g% n* Z* T* G */
) T6 y4 h! E" |& |5 g set_inode_attr(inode, sd->s_iattr);
; l! m3 d! X5 M# V } else
' g7 |) L) f; c. i# u: n% u5 W# g set_default_inode_attr(inode, sd->s_mode);/*设置inode属性*/7 C, k4 W: V' @7 S) A, ~6 m! W
' Y( n! u* ], p6 X' V3 I3 `, P8 H9 U; M& P E; R' y
/* initialize inode according to type */
' W! `1 s( s: U switch (sysfs_type(sd)) {
7 M, l% y+ v) \# G( g) D2 a) c/ Z case SYSFS_DIR:
; o& P! ^. y) m- i% |0 ] s inode->i_op = &sysfs_dir_inode_operations;
/ ?) N" t \6 j/ S# ^4 h+ Z; d inode->i_fop = &sysfs_dir_operations;
8 l! }" Z/ z3 Y5 M# t! K9 A# Y# x% A inode->i_nlink = sysfs_count_nlink(sd);0 C) z' }* d2 ]5 H9 _
break;
1 D8 Y% C# I( h/ v( P case SYSFS_KOBJ_ATTR:. c+ x$ [7 O* v9 v. C: ^
inode->i_size = PAGE_SIZE;
+ I6 w$ E2 ?& I4 I# H- M* ?$ c inode->i_fop = &sysfs_file_operations;* g4 o t- R. G" M0 U) W: w
break;
# C \. z& `. {; o2 }: P: X0 R case SYSFS_KOBJ_BIN_ATTR:
" [- b$ p; R/ o bin_attr = sd->s_bin_attr.bin_attr;3 Z7 }5 c, `3 O; y
inode->i_size = bin_attr->size;
1 b0 R6 j; n4 R# M$ p7 k5 q# a* I inode->i_fop = &bin_fops;9 D# s- D7 ?- U8 X! Z4 ~1 |
break;
) ~6 Z" f; W+ y2 ^, e) r case SYSFS_KOBJ_LINK:
+ K3 k" { K9 d inode->i_op = &sysfs_symlink_inode_operations;$ R, [8 S/ ^. R% J2 n3 ? M ?' V
break;3 S1 E) c# z" k; c( A
default:
5 ~. {9 g6 Z1 l- m. K! | x, I BUG();
( a3 z7 g, d0 [, Z, V6 W }
% z9 c- T% j( o5 \ N& p& \( u; F6 x, \5 Q7 s
unlock_new_inode(inode);( V4 q9 M3 t: i, V' x# ]
}
) t8 [: `+ z4 c9 ~该函数首先调用了,iget_locked来查找该inode是否已存在,如果不存在则创建。如果是新创建的inode,则对inode进行初始化。
( i, { W; v2 L: N* D r5 C8 z再获取了根目录的inode和sysfs_dirent后,调用d_alloc_root来获得dirent。
& c: x& X, l1 ]- W. _/**3 s( j$ C1 g. {7 N9 x* q! _
* d_alloc_root - allocate root dentry7 u1 p+ }3 a$ q7 u. s2 V( k
* @root_inode: inode to allocate the root for0 P7 @& ^$ _) p! b- a3 e- Y
*
4 {. x" x- l% d * Allocate a root ("/") dentry for the inode given. The inode is+ i( K7 F7 ]: v; [
* instantiated and returned. %NULL is returned if there is insufficient2 x. Z3 K6 i j- e! a& H
* memory or the inode passed is %NULL.
5 i; M+ z J7 j1 L- _0 f */
1 r2 X6 D$ o6 I6 R1 q
. g6 k3 `/ N% j+ ]" h" W! Astruct dentry * d_alloc_root(struct inode * root_inode)9 V7 y' b' W. { J: T* C
{, B6 V, }) e1 O3 B6 ]
struct dentry *res = NULL;
* \ T3 F7 L& S. ~$ |% ^1 E7 }# @$ m7 i0 V. Q; ~- ~; t$ p3 k0 W% p
if (root_inode) {4 H5 Q* U! r( N8 \5 ^8 K
static const struct qstr name = { .name = "/", .len = 1 };
; w; k# E$ ]2 W( u
5 J# T: n( v" I9 b! y1 `* U res = d_alloc(NULL, &name); /*分配struct dentry,没有父dentry*/
+ i9 Z$ j+ Z/ W8 m) @ if (res) {
: y+ s& n0 i1 g) v2 K" L res->d_sb = root_inode->i_sb;
7 ~" ^$ D) K \% P3 u2 n9 S res->d_parent = res; / M( p5 b+ m5 n. `: n# C$ z
d_instantiated_instantiate(res, root_inode); /*绑定inode和dentry之间的关系*/
6 U! z' ^/ j; M1 {3 i }
$ E9 J% J, {- y$ [6 v! ?+ V5 \+ e+ L }$ C/ b& A5 ~! [7 u$ t1 R8 z5 N! W' t8 `
return res;4 R% r% R/ j$ Y3 Y9 _( ]6 q5 X- T
}
0 U' i8 G# l3 _- V$ [$ [ ]1 p
/**, l. j; ?& l# ?, r7 Y5 n/ _
* d_alloc - allocate a dcache entry
7 N$ z5 b" P. }+ f# o * @parent: parent of entry to allocate4 l5 l* A7 P9 X ]. W' b
* @name: qstr of the name( s$ e6 i& X) i' f9 G
*7 G- N% F9 f+ C U" z7 r
* Allocates a dentry. It returns %NULL if there is insufficient memory) U B7 S$ h2 _: H1 o0 V
* available. On a success the dentry is returned. The name passed in is
# t4 P* p- ]! d9 g( ^: x i * copied and the copy passed in may be reused after this call.
" X. L) m! Z2 v1 }7 z* l */* ]! [0 T; I' d
0 k2 ?0 a. C- m8 S4 L5 R' |
struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)! k1 u) {: K& w2 H: t+ t Y0 _5 v
{. _' L& l! l, G0 u( k! ^
struct dentry *dentry;
- i7 i/ O8 w& t+ W) |4 G; _) W r$ k char *dname;
6 a: r/ Y$ q0 E. ^3 Y- w' z# R
h t& {9 Y) A dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);/*分配struct dentry*/& y5 V& {) `8 v
if (!dentry)1 z7 F0 B" }* x( Z- x6 h
return NULL;% h+ {6 {- m1 I9 h' J
, m" ^6 t3 m0 `! a v0 g( T6 _0 L
if (name->len > DNAME_INLINE_LEN-1) {
( Z$ P) L W( i1 z6 @+ S dname = kmalloc(name->len + 1, GFP_KERNEL);
9 ?, B! I& Z: C1 U if (!dname) {7 u$ V6 q; i3 x* K2 d! ?# f
kmem_cache_free(dentry_cache, dentry);
" O$ O0 v4 w! j return NULL;/ w$ d+ e7 j. j3 h
}) e. C! [: w" k$ f- s
} else {
7 T g t( W7 f- s' ?! q$ e6 L dname = dentry->d_iname;2 j# \6 W' b; |* d5 P
}
2 \* f/ b& Z% k( ^& L dentry->d_name.name = dname;
, E1 f- x2 x8 g- _/ k1 Y( C4 Q
9 u; u4 ]! U# D" N$ I dentry->d_name.len = name->len;2 U/ b$ ` m: D1 D
dentry->d_name.hash = name->hash;
; @3 G1 f z4 `% s/ ?) Q mEMCpy(dname, name->name, name->len);& j( v( {- O5 h y* [' r1 o
dname[name->len] = 0;
. D% j7 T" I2 Z* s# ?' d: X) P# d
atomic_set(&dentry->d_count, 1);. E+ `* Z% u' |9 R1 \9 _; s9 A
dentry->d_flags = DCACHE_UNHASHED;# n' o F! n) `+ R% M
spin_lock_init(&dentry->d_lock);
5 _0 n3 Y3 Q/ {& s" A( a6 p/ t dentry->d_inode = NULL;
" Y" x4 F T7 ] E* U6 e dentry->d_parent = NULL;
. Z- l4 I5 p4 A( Q dentry->d_sb = NULL;) W' v5 L% r) i. b! _
dentry->d_op = NULL;
F) V8 Y/ |! }: ^' u dentry->d_fsdata = NULL;
3 i+ x) ?( t4 x, W dentry->d_mounted = 0;% t0 Z) s8 }7 c# ^
INIT_HLIST_NODE(&dentry->d_hash);# @& \' r5 }$ G3 n: F, Y
INIT_LIST_HEAD(&dentry->d_lru);5 v' j7 ]$ K9 K4 M$ W9 J3 m
INIT_LIST_HEAD(&dentry->d_subdirs);5 d2 M% f( m8 j. g8 `/ N
INIT_LIST_HEAD(&dentry->d_alias);
/ _' J0 P0 h0 e
$ \/ \. x2 G2 | if (parent) { /*有父目录,则设置指针来表示关系*/9 G- o- ?. G/ y
dentry->d_parent = dget(parent);
2 H8 {2 r* G2 w* ?+ T D dentry->d_sb = parent->d_sb; /*根dentry的父对象为自己*/
6 z" n9 W3 O5 x" p- N } else {
4 L$ S- Y+ R/ [! p% W INIT_LIST_HEAD(&dentry->d_u.d_child);
7 E* |2 N0 E: v. _3 M2 F5 x: G* s }
/ C6 z7 E9 z+ }% o1 a( p7 X4 i0 \1 h; T- C7 V( e/ v
spin_lock(&dcache_lock);
$ l# {; m" i2 V& H2 \, D* A- L n if (parent) /*有父目录,则添加到父目录的儿子链表中*/
; n5 S& k5 `! Z6 I list_add(&dentry->d_u.d_child, &parent->d_subdirs);
; Z$ z* a9 {' G. ~' K; ^ dentry_stat.nr_dentry++;
# B" X+ |- Z" j8 T7 z spin_unlock(&dcache_lock);9 F9 c$ u; w9 p* D; J
9 e6 n! e5 ]" O [ return dentry;
' B- L3 N- L' J} % s# ~" l7 \& b: b1 o
- @" ^4 p$ T7 L( ^
/**4 J/ Q9 s x3 |& d* V
* d_instantiate - fill in inode information for a dentry+ K- {# z) h/ q o6 {
* @entry: dentry to complete2 E8 v: v$ D5 ?
* @inode: inode to attach to this dentry3 Y& b: ?/ Y& y* q# d$ w" ~
*
( d W+ c) T: z1 n- N8 @ * Fill in inode information in the entry.+ s* l, C. d: Y; j: z/ D5 W1 ^
*
, P& C+ b3 q0 J: d9 c: r * This turns negative dentries into productive full members
* j. u' N8 g( @) o' J" Z4 F( D1 z8 A * of society.
* v$ ~- g' G) {) \ *
, j8 G3 F5 y1 [8 J' G& k * NOTE! This assumes that the inode count has been incremented* M; ^2 P" r, D: t, A4 b) G- M
* (or otherwise set) by the caller to indicate that it is now7 @, ]9 W5 P0 I5 p7 m
* in use by the dcache.- [/ f, Q/ y3 q
*/4 \$ p# t7 \* Y, [+ Q( \2 J
( @) w- B. E3 t8 X' j- |void d_instantiate(struct dentry *entry, struct inode * inode)! p/ k$ _" y$ O* e1 Q
{
5 g3 N; o: R; C$ ~& C# p& O8 f2 g# P BUG_ON(!list_empty(&entry->d_alias));
5 k) o/ N$ v( |* ~9 p q1 q4 s spin_lock(&dcache_lock);
2 ~. O, h) }0 ?6 d" R7 W( w2 ~ __d_instantiate(entry, inode);
9 G2 L( c; l1 V# o( u spin_unlock(&dcache_lock);0 i5 w+ \% c' a" l* y, N) a
security_d_instantiate(entry, inode);. J" v6 Z" }) c- C. w3 T2 t% D
}( o6 P+ @# E: A$ t+ f7 J
6 }( L# V* p1 ]/* the caller must hold dcache_lock */$ J2 s9 ?: g+ F# k7 W6 t
static void __d_instantiate(struct dentry *dentry, struct inode *inode)
. U0 ^* e% ]/ E% h{( j: o& G& F4 D2 |2 [! N
if (inode)
* I& g k5 B1 R: o list_add(&dentry->d_alias, &inode->i_dentry);/*将dentry添加到inode的链表中*/
& d" o: n- e' B, _) X- f7 n# C8 M( [! V dentry->d_inode = inode; /*保存dentry对应的inode*/0 v4 m1 u B- A" o$ u
fsnotify_d_instantiate(dentry, inode);
1 M6 o% D3 i4 t: M2 ?}
, E7 G: \0 S+ u* T; r) e! G6 _7 i( Q/ d! i$ X. {
该函数首先调用了d_alloc来创建struct dentry,参数parent为NULL,既然是为根( / )建立dentry,自然没有父对象。# x' s( T% x2 d k1 l8 H" O
接着调用d_instantiate来绑定inode和dentry之间的关系。. J; _: A8 A1 C2 V5 c7 c. y
% n' {7 ?7 p: u7 k% o6 T) I. [
+ e) D" P5 n, J. F在sysfs_fill_super函数执行的最后,将sysfs_root保存到了dentry->d_fsdata。
8 v8 X* l+ K2 \# r `
5 E3 D7 U2 |6 @- u+ r7 r; q2 r可见,在sysfs中用sysfs_dirent来表示目录,但是对于VFS,还是要使用dentry来表示目录。
5 T* G* b$ r$ V. f: \" h
; Y3 w2 n! O" n8.2.3 do_remount_sb" C5 z1 e0 c, \# i( J
下列代码位于fs/super.c。# V5 X3 ^6 w' W+ `1 V
/**
! B! e" J6 x5 w. w( L! x I( M$ G * do_remount_sb - asks filesystem to change mount options.1 K" N" j- e9 i+ N7 o+ {* k
* @sb: superblock in question& `' C, O( z3 ?) l
* @flags: numeric part of options1 b) k% q, Q( h% `+ f
* @data: the rest of options, k* c$ @6 ~3 [7 c! w* @
* @force: whether or not to force the change- J `$ _4 O4 h$ w: g
*
& S& b. z0 }/ f4 f5 y1 U" U2 _/ ~ * Alters the mount options of a mounted file system.% A0 l& y, t0 w- Q, X! j' _
*/; I* g: ~: B- |7 B; R
int do_remount_sb(struct super_block *sb, int flags, void *data, int force)9 Y+ z) X; @2 c( X$ X4 a: W# Q
{& J! h. `! F" J6 k& Z8 Q
int retval;; W G5 f1 O& M7 H! d, H d* g
int remount_rw;
8 S- V- u1 w$ P, A, E5 o
; i) l1 o5 M2 z#ifdef CONFIG_BLOCK) y: X1 j! h/ P; G8 k* `: ?: F
if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev))
4 ~6 Y) n; ?" l9 s8 n3 W& g1 y# p, g return -EACCES;
/ Y6 b' W4 k+ I" ^#endif
" V3 B) a/ u" R8 W% s$ I if (flags & MS_RDONLY)
! g' N& A7 h* w* C# _: l acct_auto_close(sb);
# Q D5 m3 a# L$ I7 x. p9 a shrink_dcache_sb(sb);! n* g8 P: Y* n
fsync_super(sb);
7 s! n$ V* w7 w7 d4 r
: Q7 I1 n/ v. p) P# H4 T1 A /* If we are remounting RDONLY and current sb is read/write,
* d/ z0 ?! N( e5 q- a$ C, Y" V make sure there are no rw files opened */' t( f% t* t. i5 v( v
if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) {
# s6 T9 |# j5 F$ O1 b# } if (force)% M- d0 w: p) j7 j: Q: k
mark_files_ro(sb);3 c7 z+ Y/ j6 T+ a3 F/ w
else if (!fs_may_remount_ro(sb))
$ m2 K( a. I8 s return -EBUSY;
4 R4 C m. W, p" E retval = vfs_dq_off(sb, 1);
) k( h* K9 D' j3 r; R! _% H if (retval < 0 && retval != -ENOSYS)* u+ J. P- d" ?" ?
return -EBUSY;
8 r; ~ a2 `* P9 C* l }
) R4 c7 w8 S% E" {/ {5 j remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);: Q/ L3 E( X' V8 c9 S( t0 j) z
- g: f, ?/ D8 U- x( Y if (sb->s_op->remount_fs) {$ \; N5 |" ^' g; E/ k8 b9 ~1 x( r( [
lock_super(sb);
0 l5 P0 g1 h3 e retval = sb->s_op->remount_fs(sb, &flags, data);& v+ v, a! g1 u9 z6 x7 \
unlock_super(sb);2 c) t! M6 g/ y% F
if (retval)
5 X8 Y9 [3 B4 \& W return retval;7 E+ d6 k$ T( O& B8 x) P) T! v u, X2 [
}4 K9 P" P: O; E( `
sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);% _( T ?* ^8 t
if (remount_rw)
4 e I6 t3 U1 i$ y" k! H5 T8 k* W7 W vfs_dq_quota_on_remount(sb);
2 K8 c9 b* X: { return 0;
5 C# f6 }# ?( Z$ q4 S$ L}/ y. A6 ~9 R! o; d# ~, ?# W7 K
! @" r" @+ L1 N5 K+ N' q这个函数用来修改挂在选项,这个函数就不分析了,不是重点。
3 `. a8 F# D; P) u8 M8.2.4simple_set_mnt
2 a& H7 O" V# Q- X7 d下列函数位于fs/namespace.c。
; f* V+ X" m4 z# k1 y" |7 J3 }; rvoid simple_set_mnt(struct vfsmount *mnt, struct super_block *sb)
1 ^- R! p9 F4 \# j6 T( u/ i6 T3 E& M{+ W4 t% _" F) a# `' e9 u
mnt->mnt_sb = sb;
2 ~, C3 t+ Z0 ]3 s4 \' Z3 P r mnt->mnt_root = dget(sb->s_root);
/ b5 [' S/ S: R0 Q/ x6 Z}' v- Y& q. F9 J
该函数设置了vfsmount的superblock和根dentry。
' E% a+ f- t, ]3 ^
# m! ^3 S5 j7 e/ I; M8.2.5 小结2 i& Q$ e ^7 @
这里,对sysfs的注册过程做一个总结。
/ m1 s W* Z& a/ `$ i2 {9 ?$ Q$ ]8 q0 h$ o, H4 f
sysfs_init函数调用过程示意图如下:8 R& u( |- r. e6 s+ D
- D" G1 m% b' z) U# w
7 o& D& R* t! ^3 W/ d
2 z! Z- `0 ^/ ~- _7 b9 U
在整个过程中,先后使用和创建了许多struct N! v5 H5 i N; V9 M) j7 Q, Y
" ^- y+ f/ b0 k% D/ y) s
第一,根据file_system_type表示的sysfs文件系统的类型注册了sysfs。$ ]$ i) c* I& ^# \8 c$ h; ]( W1 k6 K+ E
; f) o" N5 {; ^, {5 O$ i3 z+ K
第二,建立了vfsmount。% r" J9 x) ?" h+ k, p
/ _: e$ C! {( n2 [/ u第三,创建了超级块super_block。. `% T" h% |3 x, u1 _7 w
: r# D. R' R! R ~0 n第四,根据sysfs_dirent表示的根目录,建立了inode。
; ?* T7 P- @4 ?3 `
* [- ?! x: e1 ?最后,根据刚才建立的inode创建了dentry。7 }$ F& x0 a# s# g2 ?
: a1 _! i v$ i9 P/ ^5 V除了sysfs_dirent,其他5个结构体都是VFS中基本的数据结构,而sysfs_dirent则是特定于sysfs文件系统的数据结构。8 R9 q4 Y, S0 }" {8 S
$ }/ F2 c3 i2 k' ^/ Z1 L8.3 创建目录
% E; I1 N1 i* w, W在前面的描述中,使用sysfs_create_dir在sysfs下建立一个目录。我们来看下这个函数是如何来建立目录的。& a g- d8 `& w
: c2 G" S+ p, g. y
下列代码位于fs/sysfs/dir.c。
, [/ r/ D: W9 j/**
& X6 ]( Y9 c( D( E1 u. i * sysfs_create_dir - create a directory for an object.
. z8 S% f! |7 j4 y1 r% n5 u! u * @kobj: object we're creating directory for. % L/ d+ T$ m" B! A- u# N2 @3 B
*/
/ E0 e. N/ P' @$ M; L4 ^$ hint sysfs_create_dir(struct kobject * kobj)- U3 n: m! p/ T' t' x% q1 L' u
{' V- y% P# S( h9 f4 I' E( ^6 E9 f# M
struct sysfs_dirent *parent_sd, *sd;$ z" z8 s- I# P- }
int error = 0;+ I5 z, T5 D t$ G9 z
: L! D. q) ]; h, W& |
BUG_ON(!kobj);! |) U* t2 q. G$ O0 J5 M
- S. z- |, J' M5 ^- T
if (kobj->parent) /*如果有parent,获取parent对应的sys目录*/
$ Y4 n# S4 t* S% t- Z parent_sd = kobj->parent->sd;) ]; \" o! ?, [5 D# y& n
else /*没有则是在sys根目录*/
6 O5 W0 y, w- W: T" L% E/ c, Q, D parent_sd = &sysfs_root;& ]/ T( y# @* }/ x2 k8 A2 j( u$ U
/ a) r" U4 s O& P# \
error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);* V5 x# f) }& L" D; _# y6 ?
if (!error), Y/ V4 Y2 L2 C6 |
kobj->sd = sd;
8 I' } Q) y) N% Q! T7 Y0 H- [5 [; X/ H return error;* P+ w7 J! S- A1 Z7 E% P4 L& i# j
}
; B5 H' N' B, g8 `' ?* Z0 l; j: T% ?. U8 H- y- i) F
函数中,首先获取待建目录的父sysfs_dirent,然后将它作为参数 来调用create_dir函数。
' f4 p1 K, J% `# A6 t4 i; B: d很明显,就是要在父sysfs_dirent下建立新的sysfs_dirent,新建立的sysfs_dirent将保存到参数sd中。( V, u' i% F% R% D @8 _% q
( V1 u9 B( T9 W5 C% e. e- Z
下列代码位于fs/sysfs/dir.c。
c' M* C" C7 \( {static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
: P5 b6 a5 ]$ g* r const char *name, struct sysfs_dirent **p_sd)8 Z8 e" P! \, T$ m
{
1 Z( y% S1 i2 N% g @ umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
, i/ m" k( P$ n" k3 t6 G. f1 C7 q struct sysfs_addrm_cxt acxt;7 ^7 [/ P: }% m5 ?$ n
struct sysfs_dirent *sd;
8 ?# I: L. ~- W) ~ int rc;3 p* U2 M" F- s5 ]
0 @; T4 a5 w0 R4 q+ z, ^6 B
/* allocate */ /*分配sysfs_dirent并初始化*/
% Z: p1 [; ~( n- x- Z sd = sysfs_new_dirent(name, mode, SYSFS_DIR);( |% J5 Y! a. x, H
if (!sd)
; \ q/ {3 e/ k. q2 x return -ENOMEM;
9 Q# x. M+ L% F sd->s_dir.kobj = kobj; /*保存kobject对象*/0 z( _, s" s- N0 O- i* o
' o& Q2 I- a: z2 m% k /* link in */
6 _3 j6 P1 w& q' d/ e sysfs_addrm_start(&acxt, parent_sd);/*寻找父sysfs_dirent对应的inode*/
! V% \& h4 @ j3 u' k) \ rc = sysfs_add_one(&acxt, sd); /*检查父sysfs_dirent下是否已有有该sysfs_dirent,没有则添加到父sysfs_dirent中*/
% x: E4 F7 d N sysfs_addrm_finish(&acxt); /*收尾工作*/
& U+ n0 k1 n& B, e: k( x0 o& T
if (rc == 0) /*rc为0表示创建成功*/
; |% L5 }1 ` W( v *p_sd = sd;
7 ]6 |- c$ V! Y1 n else
( ^. @+ _+ Z1 i1 Q7 ? sysfs_put(sd); /*增加引用计数*/ K2 X* ]5 o, `' E* p
/ L; q$ ^! K0 k9 _: @3 S- c" U3 L
return rc;/ H6 y$ ^$ |6 v4 S% P5 e
}
; F# {+ e9 u7 ^+ T* S这里要注意一下mode变量,改变了使用了宏定义SYSFS_DIR,这个就表示要创建的是一个目录。0 q# f1 a* z) P
' i; f. P& C( n7 [5 q
mode还有几个宏定义可以使用,如下:
. K' w" J" f8 m7 e: K#define SYSFS_KOBJ_ATTR 0x0002
# P" v3 L6 C( R( C! h/ ^#define SYSFS_KOBJ_BIN_ATTR 0x0004# ?9 S( r0 D+ \. L
#define SYSFS_KOBJ_LINK 0x0008
3 U* f+ T' E. G% H/ x#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK)
: g. o" g/ z5 G' }4 N8.3.1 sysfs_new_dirent
0 t9 k8 n% a. H1 f 在create_dir函数中,首先调用了sysfs_new_dirent来建立一个新的sysfs_dirent结构体。
/ P5 N/ w0 F' K! F" Z2 i9 }1 }9 b+ Q8 E4 A: ?- v. z; J
下列代码位于fs/sysfs/dir.c。! Q0 v- e3 Y, r$ T
struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
6 J2 ~- b* `# h& o{
/ H* T, a% V( | char *dup_name = NULL;$ G+ ?1 G2 L+ Q* s6 Y! U8 V" u
struct sysfs_dirent *sd;/ r" n, A5 V0 M5 F
6 g# x, S {6 ]5 b- X# \2 }8 B6 ?. L if (type & SYSFS_COPY_NAME) {
4 X* s# b4 _* R name = dup_name = kstrdup(name, GFP_KERNEL);3 s1 j" x" Y1 x6 ?
if (!name)* x+ ^9 f$ S; e# W" W+ j
return NULL;: i$ m7 X8 b% i8 S% P( m
}
5 o+ v: _' A5 U+ k9 D! D" j /*分配sysfs_dirent并清0*/; K3 U! n% r( Q
sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);+ q" `# R& F4 h& p }" Q
if (!sd)
( p5 e1 x7 B2 Y! w goto err_out1;3 D7 i& ]* X! t" o: d6 m# n: H
% E) r1 V/ u2 i: p* t' g
if (sysfs_alloc_ino(&sd->s_ino)) /*分配ID号*/
) r: P) t! m6 M, K goto err_out2;3 i+ W! P& ~# W6 R
. k$ p$ N' Q' _3 G
atomic_set(&sd->s_count, 1);8 |: k$ ~: |/ q$ g" \+ H
atomic_set(&sd->s_active, 0);
" R( z% S- I+ f- v1 t) Y5 ]6 `/ z2 ~' z+ V3 s
sd->s_name = name;* v4 U' z, q) I8 D
sd->s_mode = mode;7 z2 x6 Y; C( }* W/ M' @
sd->s_flags = type;
- E5 p6 o; y" C8 x/ m! n
! _& {2 U- V" f" t$ @0 d, z% e' l7 G return sd;' }/ L: Z- d3 U" z/ J3 S" L( z: N
9 _: }" v0 {( W$ L7 L* l" D# \ err_out2:
0 L6 d$ o0 ?$ }9 T0 {2 U kmem_cache_free(sysfs_dir_cachep, sd);
5 M% u8 g3 X- ^& A0 O" k6 y err_out1:8 `6 Q: h/ H' N( L
kfree(dup_name);( T+ s/ J( o" Z' q
return NULL;+ G0 h6 T5 N1 i$ K3 {. _4 a
}. o: J8 O0 k% J7 @/ ]# ]6 k
8.3.2 有关sysfs_dirent中的联合体
9 l6 P( z; I. C7 Z/ G分配了sysfs_dirent后,设置了该结构中的联合体数据。先来看下联合体中的四个数据结构。
# X( H) d* z; [* s) g2 k
" T0 V4 X m7 D. H/* type-specific structures for sysfs_dirent->s_* union members */
u. D3 J U) X3 _ \* T; Astruct sysfs_elem_dir {
4 @; Q3 K; \) `$ K" E7 r struct kobject *kobj;
6 J- @ G- {# s" R4 ]2 { /* children list starts here and goes through sd->s_sibling */# T2 ^2 m2 v% D% V! e
struct sysfs_dirent *children;8 [; l) U0 B; S, v7 l3 q& t8 Q
};
7 w8 L/ g+ v5 t" _4 E% q6 ?8 m
4 V& Z9 N s0 |# H! _3 Pstruct sysfs_elem_symlink {9 E4 Q! F' n% V' C/ D& h
struct sysfs_dirent *target_sd;
- A4 w6 u4 r: S& _4 w1 v/ g/ H};6 V) A- E. i5 Y6 k1 N. l
+ C0 T& i2 x; ]2 L0 O$ L5 e+ m3 [2 L
struct sysfs_elem_attr {
% q8 a2 o0 W& ? F/ Y struct attribute *attr;
( N& Q( Q4 X- x0 x struct sysfs_open_dirent *open;
* i! `6 I' X3 `# m. f s};+ m! v) i: \# A2 r# o/ u4 y
) c" a! l2 R3 w# l8 F0 fstruct sysfs_elem_bin_attr {
, i) h6 w: H1 N% } V5 X struct bin_attribute *bin_attr;
8 D6 k5 U# A& A. P( | struct hlist_head buffers;2 F) w9 W; P* M. ?0 ?
};
+ b0 K- H" f: l k根据sysfs_dirent所代表的类型不同,也就是目录,synlink,属性文件和bin文件,将分别使用该联合体中相应的struct。, Y3 g2 s- ~5 m5 A; c. `
在本例中要创建的是目录,自然使用sysfs_elem_dir结构体,然后保存了kobject对象。7 Z; Z* H# @ h) W9 ~
# @3 w; @ ]- d9 b$ I9 ^在8.4和8.5中我们将分别看到sysfs_elem_attr和sysfs_elem_symlink的使用。
. X# P9 f' J2 B6 q: [+ z0 N5 y2 V) a9 ^
8.3.3 sysfs_addrm_start
- c9 V6 o& H7 y) w+ m在获取了父sysfs_dirent,调用sysfs_addrm_start来获取与之对应的inode。
( A& b% J4 Q2 U, i5 y9 S1 u" y6 @+ B' f: n; x" [+ m
下列代码位于fs/sysfs/dir.c。
7 d2 s5 \5 e1 [% l2 c/**" m5 U3 \1 G) c
* sysfs_addrm_start - prepare for sysfs_dirent add/remove0 M0 Z/ u% U& x4 N! L" m
* @acxt: pointer to sysfs_addrm_cxt to be used* c' K4 w- R$ ?9 a% A1 C
* @parent_sd: parent sysfs_dirent: K, q6 c& g7 y0 g0 E3 V+ Q
*
2 B' @5 I2 O. i( @! ] * This function is called when the caller is about to add or
4 v C& a; w' C& H) ~0 P/ z * remove sysfs_dirent under @parent_sd. This function acquires
! N" | g% L% I- @# z _ * sysfs_mutex, grabs inode for @parent_sd if available and lock7 D$ J0 ]2 \. X
* i_mutex of it. @acxt is used to keep and pass context to% Z2 F; | E7 H' [7 T2 q, |, I
* other addrm functions.
6 d h& c7 \7 }2 o5 y *
( c( [8 E! p; N1 p# y7 O; n * LOCKING:
/ w& `; P/ Q7 B# e Q4 [. ~ * Kernel thread context (may sleep). sysfs_mutex is locked on
: Z7 b$ X' i+ @4 V2 c * return. i_mutex of parent inode is locked on return if
: T% }/ l4 \4 s& u4 @ * available.
' S6 R* O) x6 Y3 k9 i */
7 _3 Z8 M5 `$ a/ pvoid sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
) e- f" `" A. l; V+ M struct sysfs_dirent *parent_sd)1 k* O# \5 r! T) E/ `2 M! l0 Y
{
) D6 z% w4 Q h1 } struct inode *inode;; A; D/ x3 t) N4 `; P& m
- b3 F8 z+ h, m, p3 k1 V memset(acxt, 0, sizeof(*acxt));
) a% U, {3 v; k2 z. x acxt->parent_sd = parent_sd;- v$ E# w# k# I" a
8 e( y% g3 v" P1 O
/* Lookup parent inode. inode initialization is protected by, V8 K. [" `+ N) l4 _( b
* sysfs_mutex, so inode existence can be determined by& U" K! s+ z8 `: x& z8 o7 w
* looking up inode while holding sysfs_mutex.% v: G* a$ L9 N$ i
*/0 J5 \% k5 z& F
mutex_lock(&sysfs_mutex);/ w0 S$ u& Y4 v0 J* W2 J' X
/*根据parent_sd来寻找父inode*/
% B) g' X' t: x" W( Z inode = ilookup5(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
2 k# q0 W# q6 y8 q# ?) b& L5 n4 e parent_sd);; D/ v) c, z& S7 T" c8 _& U- I
if (inode) {# L0 f: T1 c& C$ S
WARN_ON(inode->i_state & I_NEW);) e8 D! E' S+ Y4 `6 B( n
7 b, M2 M" ~ @' p /* parent inode available */
! m8 }2 ]; Z4 g" e1 J. h acxt->parent_inode = inode; /*保存找到的父inode*/$ ~; x$ _& j/ [2 a4 g. f
; @' o1 u f/ _& k5 _+ Y! s /* sysfs_mutex is below i_mutex in lock hierarchy.2 V/ m3 ]3 x2 x" _' {* k# K
* First, trylock i_mutex. If fails, unlock
2 R9 ]9 [( [& O4 g5 U. B$ R * sysfs_mutex and lock them in order.3 c# Z L/ M0 j9 \( J& D7 ]& I
*/
, Z q K5 d& t2 ?% n* w if (!mutex_trylock(&inode->i_mutex)) {* L6 W, ]- j7 ?: R X* N
mutex_unlock(&sysfs_mutex);
3 I1 T. ^, N3 |+ t( G# C mutex_lock(&inode->i_mutex);; N' l/ ^$ } w% \& M
mutex_lock(&sysfs_mutex);
4 R, j" t' [$ h* j z) S }5 Z3 T) J1 U2 s0 n& y% ?
}+ C/ v& ~ ~/ p% d9 i5 {# v
}% \8 o/ B5 B3 L7 o: l
" e. g+ }2 s8 j# o" H' e, A
/*4 n" J, m# R8 X+ f8 F
* Context structure to be used while adding/removing nodes.
3 K5 l$ ]/ m6 H4 A2 U: w. y, ^ */. t4 x& | A2 g" S: ^% U5 \
struct sysfs_addrm_cxt {6 {5 u Z. o: u( q
struct sysfs_dirent *parent_sd;; ]7 l! ?/ R+ z6 c, e7 Q/ i
struct inode *parent_inode;7 u0 d: v* D) d% w& S( y
struct sysfs_dirent *removed;% D9 @# Y0 H ~" M
int cnt;
9 x/ _. ]. B5 K: ^5 \- l};! v% ?. v0 F' z- a# H% l( W5 {
注意形参sysfs_addrm_cxt,该结构作用是临时存放数据。
9 y( D7 b7 l4 J+ w$ l# t, K8.3.4 sysfs_add_one6 S9 |2 Q# b- S
下列代码位于fs/sysfs/dir.c。
3 V1 }) U4 `3 ?0 j& R/**
" e& | w6 W3 j3 j * sysfs_add_one - add sysfs_dirent to parent
" A$ e9 z+ w( f5 `" k* F * @acxt: addrm context to use( C; b3 C; r( n+ D* y6 K
* @sd: sysfs_dirent to be added0 n0 t+ j6 ~+ ?. W' Y; _. k# N
*
. G9 Z+ Z2 W; ~- g * Get @acxt->parent_sd and set sd->s_parent to it and increment* O( c: ~! S5 k( @; Q; {9 f @0 ~
* nlink of parent inode if @sd is a directory and link into the7 d* E% C5 G/ A$ ^+ b
* children list of the parent.
% f3 E3 X; O& y+ x+ {* K *
$ V, {4 {) B* N' f+ ^: B1 a * This function should be called between calls to) g% P' h1 q- ?8 D
* sysfs_addrm_start() and sysfs_addrm_finish() and should be& t# F2 r8 [2 Y! F x |" U2 b4 T
* passed the same @acxt as passed to sysfs_addrm_start().
8 e, l/ I* b6 a. i1 c *
" d3 v0 C6 s! m; s+ ^ * LOCKING:4 o) R8 H5 T) |: m6 [9 p
* Determined by sysfs_addrm_start().0 b( Y/ z1 y4 K4 k5 ]: _3 D
*
1 h" \9 D8 H# N * RETURNS:
4 d* ]! Q9 W! q8 _, T* C * 0 on success, -EEXIST if entry with the given name already O- ~ Y2 C& n! c7 M
* exists.
$ n( ^- |7 Z: t' _3 U& d7 H */
5 B9 {9 q$ P2 V9 sint sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)9 t8 O K: n% ~ |6 \2 r
{6 ^! v* e" L {6 b/ u, z: `
int ret;
' ?$ q% L- K& m) j
4 m+ m# d: p- y- ~' P ret = __sysfs_add_one(acxt, sd);& p0 }/ t4 E) ? e" t! b
if (ret == -EEXIST) {
# E3 M7 J/ u/ S+ ~3 `) D char *path = kzalloc(PATH_MAX, GFP_KERNEL);5 r6 l/ s; o E* N% F
WARN(1, KERN_WARNING4 T) \- q; U# j8 f. j$ ]
"sysfs: cannot create duplicate filename '%s'\n",
. h6 Z& _6 B: z5 E2 q6 C% z( \4 h/ b (path == NULL) ? sd->s_name :0 W; |( L" i, |( }4 C
strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"),3 k& \, |* n0 {' }& C" T
sd->s_name));4 w/ a" j$ |& U( k: X
kfree(path);
2 W/ j2 Q& t* t9 M8 \6 c }
. I; x* E, O( l* ^' y( f# L) f$ H P3 O% j( ?+ M9 L) t8 \5 k
return ret;
! D& P- ^; b$ I# c' L: V: @}6 |: u/ {0 F; E: x" z
8 R7 N8 W5 m5 f9 q4 w, ?* @* c% N/**
2 D4 ^* `: c4 n. {% w * __sysfs_add_one - add sysfs_dirent to parent without warning
$ v0 S/ S) _7 V9 U4 D * @acxt: addrm context to use/ e8 c4 Z5 j- p7 h( E/ x: k
* @sd: sysfs_dirent to be added
/ L! N( i7 U- `# _( N *. ^! g$ U' i% q0 _( C* c& V
* Get @acxt->parent_sd and set sd->s_parent to it and increment5 l2 @& K Z6 g7 q" D q3 N
* nlink of parent inode if @sd is a directory and link into the
7 I x& U1 |( E7 [% d' H" a * children list of the parent.
4 z: W6 f# K9 X- G7 D1 k8 z/ b: S$ k *: g" W H3 H8 z+ h. o: p8 E9 q, R
* This function should be called between calls to1 t9 p' ~0 i3 q( l' |! G9 E
* sysfs_addrm_start() and sysfs_addrm_finish() and should be
h; Y6 S6 |- l6 O* R' w& }8 t * passed the same @acxt as passed to sysfs_addrm_start().
3 e, W4 D5 Z( e3 U5 u *- Z0 w6 B( {/ p$ [) C: p5 O
* LOCKING:) w! Q$ u& l: L c
* Determined by sysfs_addrm_start().
1 Q& Q7 p" c" B2 p *2 P$ l; }( v# u# K' \( n
* RETURNS:5 @& i* I- k" Z U
* 0 on success, -EEXIST if entry with the given name already
& s( \. K" S3 B! Y9 j6 ^+ U * exists.
; I5 E5 f' M q. l& U */9 q S% v# O' U l/ f& H& f
int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
4 w# S! w9 K& O e3 F' X2 l{
, l$ t/ M3 F9 b" o/ v /*查找该parent_sd下有无将要建立的sd,没有返回NULL*/. q/ j$ Q# m( y" p' U
if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
# H, n! u. s) y3 O" I2 H% r, ] return -EEXIST;
! q" `& n( B7 Y; p5 W% Y; R. E7 `( E1 [; Z! \3 F
sd->s_parent = sysfs_get(acxt->parent_sd); /*设置父sysfs_dirent,增加父sysfs_dirent的引用计数*/, O; ~9 a, d" X1 H. m
# N7 M1 H" K& E4 M
if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode) /*如果要创建的是目录或文件,并且有父inode*/4 f9 B0 {8 j+ K
inc_nlink(acxt->parent_inode); /*inode->i_nlink加1*/
+ i$ _- Y! c6 o# g% Z9 x" g6 C
0 B7 a2 s# I9 n' J acxt->cnt++;" d% F7 E7 [/ m: r* N+ z' h# R' z
- j1 m2 V7 C* P! {/ J8 t sysfs_link_sibling(sd);
1 K d& O% Z1 X2 j6 A! i0 u# A9 ]5 q% z$ H @" T7 X
return 0;$ K! I# S! V6 S' b
}1 A- ~, a1 k7 X) c7 _# n
& X8 I1 G. r$ U' I/**
' Z. @+ m9 C4 d$ P4 F * sysfs_find_dirent - find sysfs_dirent with the given name
7 v! i. B. M1 c! @ * @parent_sd: sysfs_dirent to search under; m* A0 \8 b: e9 o! I& G" ]
* @name: name to look for
( {; p6 _' l! \0 }8 n6 C *
$ C" D j* L$ o& X$ K * Look for sysfs_dirent with name @name under @parent_sd.# Q5 G9 X7 H( K% \2 e6 k% U0 o
*
0 L; P: P& Z4 E1 j- H * LOCKING:
, |4 [' t$ X* O. V6 f * mutex_lock(sysfs_mutex)
1 v: {6 v- a# V c0 r; U# b: n *) k7 w: `& H; Y! D+ B2 |: i ?
* RETURNS:2 j8 z4 c. e/ ~1 J$ {8 W% D# m. y% ]
* Pointer to sysfs_dirent if found, NULL if not.
8 ^& f7 b2 R5 v3 {1 R$ ^( m+ b */
3 f& b7 T0 W. d; M( ystruct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,8 S2 |: @, [. f
const unsigned char *name)8 ^6 ~7 h! F/ `0 ^+ P
{, m& D2 S1 @4 d, J
struct sysfs_dirent *sd;& N: p! j+ X. }- h6 A8 k) B8 }+ V4 E
* d1 v7 `0 N6 Y# e
for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)0 {" s3 d3 P' `4 H1 |& P% ^
if (!strcmp(sd->s_name, name))2 E7 E5 p2 o3 z' j/ R
return sd;2 U) _, x/ z" M& i
return NULL;
( v0 Q0 u$ v4 u6 h. R3 b} 5 ^- P4 [& t, w f2 y, A
K2 |0 L! Z7 q6 j9 ^$ w5 D+ n/**
! x6 z8 x) [, C1 l) B! e * sysfs_link_sibling - link sysfs_dirent into sibling list o4 n" {$ q' a# K8 y
* @sd: sysfs_dirent of interest
& ~7 B" A/ _0 p$ G G+ B! I *
+ D) q) Z0 R+ z% h$ n * Link @sd into its sibling list which starts from" j: J2 I$ ?# N2 ~- J
* sd->s_parent->s_dir.children.
' d4 m. {, ~+ ?* I *' k$ {8 i: d( z- X
* Locking:; p7 E9 f# W+ n$ i; q
* mutex_lock(sysfs_mutex)
1 R: W& |3 N x9 O! d$ F D7 C( J */
$ R+ T3 }1 i: G* U, r. astatic void sysfs_link_sibling(struct sysfs_dirent *sd)
" u/ I! v) \# _5 A( z/ V# q% {{
7 r! b3 b j H/ S struct sysfs_dirent *parent_sd = sd->s_parent;/ t; C9 V" g4 S; S: D
struct sysfs_dirent **pos;
: i# T! j4 s" M" m' r- Q2 D( y0 K7 Z I) T9 T
BUG_ON(sd->s_sibling);' m+ `' L& d8 @& [
7 Y1 s) [9 L9 ] b% H6 @ /* Store directory entries in order by ino. This allows
* ]! J# R' L$ w" { * readdir to properly restart without having to add a
" D: }' t' f0 l N9 o6 }( a * cursor into the s_dir.children list.* l& F& x8 y% k: U6 u
*/! W0 d; M0 L, x
/*children链表根据s_ino按升序排列,现在将sd插入到正确的儿子链表中*/
5 s7 ?8 e/ k$ Q) z for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {
`% Z' N7 U6 g0 t% L, ]8 Z8 ` if (sd->s_ino < (*pos)->s_ino)8 h* V$ S/ j. q; E
break;' O1 L1 d! l& s4 `- }; j
}! J* _2 m! r% Z; d" i+ \, d
/*插入链表*/& a) b4 g' E# ^8 O
sd->s_sibling = *pos;8 ~ k5 u0 i7 M( G7 D9 \
*pos = sd;
$ s/ `1 J0 i* A/ X# v- }7 g1 f}) ]1 i+ V0 @. H- I
该函数直接调用了__sysfs_add_one,后者先调用sysfs_find_dirent来查找该parent_sd下有无该的sysfs_dirent,如果没有,则设置创建好的新的sysfs_dirent的s_parent字段。也就是将新的sysfs_dirent添加到父sys_dirent中。接着调用sysfs_link_sibling函数,将新建的sysfs_dirent添加到sd->s_parent->s_dir.children链表中。
; }; \+ O. b8 {& }, O6 E1 R8.3.5 sysfs_addrm_finish
$ j( n# v4 I9 z, K3 T, a% z下列代码位于fs/sysfs/dir.c。$ d0 c* u/ o7 G z. W
2 z9 e9 n; `: d2 i2 O0 Z: ]9 C
/**
& A7 s9 S* ?, g1 F" Y" i$ b2 V * sysfs_addrm_finish - finish up sysfs_dirent add/remove
D, b- d& G: v$ u * @acxt: addrm context to finish up
- C! L& r5 D& b7 |; g) O *" U) j3 G3 @2 V& y7 s
* Finish up sysfs_dirent add/remove. Resources acquired by; n6 b6 N& s7 Z: V g) C2 C! o
* sysfs_addrm_start() are released and removed sysfs_dirents are
( } i3 p1 S4 }1 G * cleaned up. Timestamps on the parent inode are updated./ R- x0 ?# u# t! j5 g- S
*
0 d+ B& l8 m' D * LOCKING:; ^+ ^: ?& A7 v
* All mutexes acquired by sysfs_addrm_start() are released.
5 @5 K& r$ O5 n% L */
" m3 W" g# g& u; K- p3 @4 avoid sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt). u5 @6 _. q8 r2 m6 B" j
{
! _! m5 k. l' D. s! p3 L /* release resources acquired by sysfs_addrm_start() */
" O5 E2 Z; R5 A! W mutex_unlock(&sysfs_mutex);! T. ^. ^* M* Q, m$ S4 n2 l7 w/ E
if (acxt->parent_inode) {
& ?# B; n9 _6 J struct inode *inode = acxt->parent_inode;+ O" X& l; j) F
& I; [6 J k W8 g5 s/ s9 Y' z /* if added/removed, update timestamps on the parent */) V/ S1 L! t; v- {
if (acxt->cnt)
- O+ U8 a6 V' x, n0 J, { inode->i_ctime = inode->i_mtime = CURRENT_TIME;/*更新父inode的时间*/
, @ n+ {7 a Q9 \( u
9 C$ C5 i5 w. s5 D( j7 p mutex_unlock(&inode->i_mutex);. k- M: b" T- d1 }0 g) R
iput(inode);
: E- T* a# s) L7 i4 d P }/ v) B; i+ L! r
, L: h( [/ U& a) `. K# s /* kill removed sysfs_dirents */
" q& X, G; d* m" U while (acxt->removed) {. i N; C& \$ ~6 n
struct sysfs_dirent *sd = acxt->removed;5 z& O4 J! s K4 ]6 J5 B, E/ P
4 v9 D. T: R& R1 a; U/ g) ^ acxt->removed = sd->s_sibling;
8 Y% r% w R2 D# b0 P9 f" o- p sd->s_sibling = NULL;1 b$ p2 d0 C5 |, a( J
- I. J4 |9 J- v3 Z$ h' j
sysfs_drop_dentry(sd);/ s% a8 {, \7 k
sysfs_deactivate(sd);
" o7 }# @% g1 H G6 } o unmap_bin_file(sd);
: v8 `% Y6 O: k sysfs_put(sd);
0 N! i8 X5 x5 Q# ? }: T& F5 r1 H0 U) @# [+ i
}6 X* ~) X' X. q& ^0 [
, M# I2 t. M) z3 j4 u1 k该函数结束了添加sysfs_dirent的工作,这个就不多做说明了。, {1 x, F. O* i6 w; M: C
3 d% v0 X7 u0 f3 a3 ^& e# d
至此,添加一个目录的工作已经完成了,添加目录的工作其实就是创建了一个新的sysfs_dirent,并把它添加到父sysfs_dirent中。( @( @7 Y* y Q# S
) Q6 J+ {& X5 T3 t8 y% a1 U下面我们看下如何添加属性文件。" _1 {# Z# W( x0 o! u: R' h
8.4 创建属性文件, |( [$ e/ Z7 a$ @' g
添加属性文件使用sysfs_create_file函数。! Q$ a+ w" K) `
* N* G9 ~ b* P! x下列函数位于fs/sysfs/file.c。
- D } e; F# w u/**
1 ` O% S% A9 S/ l9 j# x4 z7 H * sysfs_create_file - create an attribute file for an object.
7 M$ b2 e8 q, w& ]5 u * @kobj: object we're creating for.
0 z* r5 V' w+ G$ Z- E! y * @attr: attribute descriptor.$ W$ M# ?8 |" {: B A! `
*/
: h1 ~2 u8 @7 u. s/ z" p" s+ n: B$ u1 q8 N3 e1 B) n3 K
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)5 h, c2 E2 M1 o9 K) l1 X
{4 T9 Y0 `5 _. I. z" i
BUG_ON(!kobj || !kobj->sd || !attr);7 i6 W; x4 T0 y; M4 L
$ n5 @* f: ^% X/ q0 ~" V5 { return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
$ K- E& s2 I" J% l- z
! d3 _6 @6 H T3 e9 p3 k* v}. r% W, O2 [$ T7 s+ y9 R
! z, }3 B1 t) C: Gint sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
+ s8 a0 r8 P8 S/ L int type)% t, b8 i( Y2 o
{- Z3 s- r1 c- |6 L2 ^
return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
: \8 O1 r' q/ f; M}' ^# K( m/ F$ X. Y; j* S
& J! Z# `' D1 c2 ^/ d, J
int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
% S$ ?: F6 Z3 v, ^ } const struct attribute *attr, int type, mode_t amode)
- ?/ h( F# O$ s{6 K: d& e$ Y3 O8 @
umode_t mode = (amode & S_IALLUGO) | S_IFREG;
3 i6 e u+ R% ~9 w$ w struct sysfs_addrm_cxt acxt;
3 I6 `/ m- G Q) } struct sysfs_dirent *sd;
6 @& q* J" h# h: F int rc;
: \, X: }* t5 i# M: b$ O /*分配sysfs_dirent并初始化*/" a3 t# O7 d4 C: ~' D3 C# T S
sd = sysfs_new_dirent(attr->name, mode, type);
% c* @, T2 S; ]8 g" U% H0 m if (!sd)
?$ ?: ~+ H3 l3 u return -ENOMEM;
, v( Z& N) C% s1 S* `+ I: d& U sd->s_attr.attr = (void *)attr; L$ p$ e- z, i; U( r5 u
. H9 D! o9 ?7 I( _: c5 J sysfs_addrm_start(&acxt, dir_sd); /*寻找父sysfs_dirent对应的inode*/8 w- }) {0 A1 P$ {3 }/ z0 J! c
rc = sysfs_add_one(&acxt, sd); /*检查父sysfs_dirent下是否已有有该sysfs_dirent,没有则创建*/
% Y/ Y- Z9 R: O0 [! l/ m f sysfs_addrm_finish(&acxt); /*收尾工作*/. n. F0 f, x: i% L
4 J- A2 z/ s* e2 P
if (rc) /*0表示创建成功*/
* l& E8 U& ~0 F' T# t( Z) \3 o sysfs_put(sd);5 H& _# e6 d9 ]' @% ]( u
0 a6 {) J. ~1 D0 a return rc;
1 b8 k/ u9 {5 l7 ^}
- ^! x6 U) L5 i4 O- X9 Q& \+ `* ?) d, W" c9 E! b
sysfs_create_file用参数SYSFS_KOBJ_ATTR(表示建立属性文件)来调用了sysfs_add_file,后者又直接调用了sysfs_add_file_mode。5 h5 p7 K2 J% l
sysfs_add_file_mode函数的执行和8.3节的create_dir函数非常类似,只不过它并没有保存kobject对象,也就是说该sysfs_dirent并没有一个对应的kobject对象。% ?2 r6 X' [( y2 M# [$ u$ [
3 h4 x/ W r2 z1 u# e2 ?5 V需要注意的是,这里是建立属性文件,因此使用了联合体中的结构体s_attr。# W* O, A: I5 {7 \
8.5 创建symlink5 O1 H. ^# X& X% o
最后,来看下symlink的建立。9 p. z, ?- [ i) t" V" o7 K A+ M
/**
7 O9 R: N* h7 c2 m: n+ |( ]1 T * sysfs_create_link - create symlink between two objects.0 @, [2 ]; w1 `6 ^ L% Q& T& M- s/ j
* @kobj: object whose directory we're creating the link in.
$ u' v, ?, v ]4 ~+ v y6 l \ * @target: object we're pointing to., a+ m1 j3 q: g5 x6 e1 P- a
* @name: name of the symlink.
7 I2 s, U/ w! m6 U */# H8 _0 O! k8 k- e4 O, F* d7 ~! i# Q
int sysfs_create_link(struct kobject *kobj, struct kobject *target,0 A, z8 W" `2 P
const char *name)
' t- O V& d* T- ~# ~2 D{
1 d7 J! Y, n2 X# t$ \ return sysfs_do_create_link(kobj, target, name, 1);1 ]% ^3 Z( `/ O5 X7 Q/ [, _ l
}
8 F u) y% z7 F- @
+ ]! l8 \# p# e& s2 Istatic int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
* G# E3 v/ f' h* V6 y' R0 h const char *name, int warn)
4 @2 F- i1 x8 \% A6 T; [; |' a{
. Q5 F- R- `6 V8 L" X struct sysfs_dirent *parent_sd = NULL;# @; k; H I9 E/ {4 x" M2 \/ c
struct sysfs_dirent *target_sd = NULL;
- M8 _1 U) A ?& n# @ e struct sysfs_dirent *sd = NULL;! |! T5 y- ]1 s2 g
struct sysfs_addrm_cxt acxt;
9 i+ b+ L/ V1 u. N \! t int error;
& ?9 e& w3 H4 B$ {% e$ |. y
2 k5 Y0 _% g9 f8 _% M BUG_ON(!name);
$ u& k. d' c$ {
G u0 R; |1 | if (!kobj) /*kobj为空,表示在sysyfs跟目录下建立symlink*/
& I3 [% w) t! K* Q( L: R parent_sd = &sysfs_root;' \# e, u& `1 z6 N1 g
else /*有父sysfs_dirent*/6 k* ~) F5 L$ a. @5 V! J! Q; b
parent_sd = kobj->sd;
& L2 ?& s: a3 {! c* u' h9 h5 n% }( f6 Q `, Q. x' V' \
error = -EFAULT;2 C; }7 r) Z7 u7 o! E% M, A7 @, V) V
if (!parent_sd)
0 j' u* V( w1 y/ F' E+ D' }' y goto out_put;' m# B: g; Y& z; ]1 `' L' s F: d
3 g9 b ^, }; Y5 n( m" p
/* target->sd can go away beneath us but is protected with2 S3 m& {8 P& ~; p- h9 D
* sysfs_assoc_lock. Fetch target_sd from it.$ c4 _4 _. P9 d: H6 [ Q+ \
*/4 j& ]- t6 b/ o
spin_lock(&sysfs_assoc_lock);
3 s' r( p7 ]% R+ u; M if (target->sd) A( N8 N: M- {7 _! }
target_sd = sysfs_get(target->sd); 、/*获取目标对象的sysfs_dirent*/
7 N8 N" p7 X4 Z6 g spin_unlock(&sysfs_assoc_lock);
# g" a8 P5 n% ~' D" {6 b
0 m2 z7 O8 b3 ~5 P8 Y! P0 s0 Y error = -ENOENT;
1 v- w6 I0 A& T8 A# f if (!target_sd)
/ {! B8 K2 |) Y. } goto out_put;1 `: x( r6 s+ V. D* c# F
9 u9 G# n' D/ | error = -ENOMEM;
" b* ]9 X- {& T4 W7 g2 [9 u/ S /*分配sysfs_dirent并初始化*/4 H. O) ~4 b" s: {
sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
1 l' s3 @# v6 W& J3 X* u if (!sd)% J0 Z1 g( s/ j) P
goto out_put;
[: U9 D+ x) L0 m$ u* ~* _: a, H/ {6 P3 `$ J2 J- A
sd->s_symlink.target_sd = target_sd;/*保存目标sysfs_dirent*/8 ~3 S' f5 |* T% s5 r
target_sd = NULL; /* reference is now owned by the symlink */
- j- ^ ~6 t; } c: r# n" N
. n5 u/ S; i8 E6 T, S sysfs_addrm_start(&acxt, parent_sd);/*寻找父sysfs_dirent对应的inode*/
! |- w/ {& `& c* U; D if (warn)
8 N/ A- x b8 h7 M5 E2 r' B3 ?9 b error = sysfs_add_one(&acxt, sd);/*检查父sysfs_dirent下是否已有有该sysfs_dirent,没有则创建*/
0 [8 d6 n1 i- p) z6 `. S4 o) P3 J& o else, H- v# f0 z+ P, I4 o) X( V) l5 K
error = __sysfs_add_one(&acxt, sd);
) h) \# C9 ]' ` sysfs_addrm_finish(&acxt); /*收尾工作*/
: Z* Z5 g! v4 e
5 j# R1 @. z/ H" A) X% U8 `* P if (error)& k/ e8 X, a5 F+ q, p
goto out_put;8 ?9 @( g. a+ X% K; K/ G0 Q
3 L# G# c F6 p$ n+ S& q% L1 f
return 0;' @, K! }7 L9 a0 o" T( [. r
$ U0 I4 v- J+ u8 U out_put:
- [) N9 h9 w9 N l6 Q( W sysfs_put(target_sd);
) ~7 S) P& J ^ sysfs_put(sd);# n, s- ^( a' C! A G7 U
return error;. d9 _7 G3 U2 X
}
+ D: j( G" a0 j2 D& r. O+ M" G2 W" g
% b* H, X( w% s0 U/ }2 b: l. v/ b这个函数的执行也和8.3节的create_dir函数非常类似。其次,symlink同样没有对应的kobject对象。9 q1 v8 A- d. Q) i- V
因为sysfs_dirent表示的是symlink,这里使用了联合体中的s_symlink。同时设置了s_symlink.target_sd指向的目标sysfs_dirent为参数targed_sd。
0 S1 P: ]+ m8 x: a
: ]- |$ ?: T5 L; N8.6 小结
& e" v) b' y6 ^0 e4 x本节首先对syfs这一特殊的文件系统的注册过程进行了分析。接着对目录,属性文件和symlink的建立进行了分析。这三者的建立过程基本一致,但是目录# \) b) k4 u, A% K0 g
6 ]8 {6 ^# H& S" E有kobject对象,而剩余两个没有。其次,这三者的每个sysfs_dirent中,都使用了自己的联合体数据。
' y- p4 ~ W: j" ~: y4 A4 P0 p
5 H. Q' f% [- I/ W6 T7 {8 |9 总结
& G7 Z `* [) d5 D' q, F! }4 k% ~* c9 I本文首先对sysfs的核心数据kobject,kset等数据结构做出了分析,正是通过它们才能向用户空间呈现出设备驱动模型。! I9 K. D0 N; X5 q) |% t. n- P D
! y8 B. B9 i4 Y5 i% \: j# k
接着,以/sys/bus目录的建立为例,来说明如何通过kobject和kset来建立该bus目录。
2 ~( Y) k* l) c: Y% c
5 H C8 R% b3 f. h! r/ O随后,介绍了驱动模型中表示总线,设备和驱动的三个数据结构。8 B# B& b5 z' W" o9 a
3 d$ W0 X0 E; n, [3 C2 ]. s# \ t
然后,介绍了platform总线(bus/platform)的注册,再介绍了虚拟的platform设备(devices/platform)的添加过程。9 s6 Q+ K8 {1 \, {. A; I% \; R$ K. n1 D
5 L& F7 A0 Z( k4 r) L- Y( l, y
之后 ,以spi主控制器的platform设备为例,介绍了该platform设备和相应的驱动的注册过程。
/ G2 \( Z5 Q- l7 c5 y4 q( b, v- a3 X+ }5 K' F ]: D8 g
最后,介绍了底层sysfs文件系统的注册过程和如何建立目录,属性文件和symlink的过程。
( }. P& N8 V1 c1 V* X* Z2 S& r4 n% ^+ w
- i+ N0 Z5 K5 M' `7 d
|
|