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