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