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