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