I2C子系统之内核中I2C子系统的结构
I2C子系统之内核中I2C子系统的结构
本文开始,分析内核的i2c子系统。
说明:1.分析的内核版本为2.6.37.1
2.开发板为TQ2440,板载ARM9(S3C2440)
3.I2C设备为AT24C02
4.分析顺序就是内核I2C子系统的注册顺序(即本系列文章发表的先后顺序)。
在正式进入代码分析前应摸清各初始化函数的执行先后顺序,清楚这个顺序后对i2c的整个框架也有就有数了。
1.初始化函数的执行顺序
1.1 函数执行顺序是如何确定的
内核编译链接完成后初始化函数的执行先后顺序就确定了。这是通过链接器在链接时调用链接脚本/arch/arm/kernel/vmlinux.lds来完成的。脚本规定了不同代码段,例如_init、text、data等不同属性的代码段存放的位置。假如同属_init函数A()和函数B()就全部放在脚本中定义的_init地址上,但是具体是A()函数在前还是B()函数在前呢?这是由目录下的Makefile文件来决定了。Makefile文件中函数存放的先后顺序来决定了,假如obj+y = A()在obj+y = B()之前,则最后连接的时候A函数就在B()函数之前执行了。所以决定函数执行的先后顺序是由1.vmlinux.lds链接脚本2.驱动目录下Makefile文件共同确定的
首先分析链接脚本中关于顺序的控制。
vmlinux.lds:
- #ifndef __ARMEB__
- jiffies = jiffies_64;
- #else
- jiffies = jiffies_64 + 4;
- #endif
- SECTIONS
- {
- 。。。 。。。
- INIT_CALL
- CON_INITCALL
- SECURITY_INITCALL
- 。。。 。。。
- }
- #define INITCALLS \
- *(.initcallearly.init) \
- VMLINUX_SYMBOL(__early_initcall_end) = .; \
- *(.initcall0.init) \
- *(.initcall0s.init) \
- *(.initcall1.init) \
- *(.initcall1s.init) \
- *(.initcall2.init) \
- *(.initcall2s.init) \
- *(.initcall3.init) \
- *(.initcall3s.init) \
- *(.initcall4.init) \
- *(.initcall4s.init) \
- *(.initcall5.init) \
- *(.initcall5s.init) \
- *(.initcallrootfs.init) \
- *(.initcall6.init) \
- *(.initcall6s.init) \
- *(.initcall7.init) \
- *(.initcall7s.init)
- #define INIT_CALLS \
- VMLINUX_SYMBOL(__initcall_start) = .; \
- INITCALLS \
- VMLINUX_SYMBOL(__initcall_end) = .;
这只是连接脚本的中的语法,函数中具体是需要宏来给函数做上标记的。
具体在/include/linux/init.h中,相关代码如下:
- #define __define_initcall(level,fn,id) \
- static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" level ".init"))) = fn
- /*
- * Early initcalls run before initializing SMP.
- *
- * Only for built-in code, not modules.
- */
- #define early_initcall(fn) __define_initcall("early",fn,early)
- /*
- * A "pure" initcall has no dependencies on anything else, and purely
- * initializes variables that couldn't be statically initialized.
- *
- * This only exists for built-in code, not for modules.
- */
- #define pure_initcall(fn) __define_initcall("0",fn,0)
- #define core_initcall(fn) __define_initcall("1",fn,1)
- #define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
- #define postcore_initcall(fn) __define_initcall("2",fn,2)
- #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
- #define arch_initcall(fn) __define_initcall("3",fn,3)
- #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
- #define subsys_initcall(fn) __define_initcall("4",fn,4)
- #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
- #define fs_initcall(fn) __define_initcall("5",fn,5)
- #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
- #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
- #define device_initcall(fn) __define_initcall("6",fn,6)
- #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
- #define late_initcall(fn) __define_initcall("7",fn,7)
- #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
- #define __initcall(fn) device_initcall(fn)
- ... ...
- #define module_init(x) __initcall(x);
原来module_init就是一个宏,可以发现module_init宏最终展开后会将init_func函数做个
initcall6.init的标记,最终此函数就被链接在此initcall6.init的位置,而那些序号大initcall6.init的函数
则存放在被module_init修饰的函数之前的位置。
Makefile文件的控制
i2c子系统的相关源码集中在/driver/i2c目录下
所以此处只从/driver/i2c目录下的Makefile文件分析
/driver/i2c/Makefile:- obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
- obj-$(CONFIG_I2C) += i2c-core.o
- obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
- obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
- obj-$(CONFIG_I2C_MUX) += i2c-mux.o
- obj-y += algos/ busses/ muxes/
- ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
1.2 i2c子系统的初始化函数的执行先后顺序
结合vmlinux.lds和Makefile,可确定i2c初始化函数的执行顺序如下:
1./dricer/i2c/i2c-core.c中的函数:i2c_init() postcore_initcall级别
2./arch/arm/mach-s3c2440/mach-smdk2440.c中的函数:smdk2440_machine_init() arch_initcall级别
3.driver/i2c/buses/i2c-s3c2410.c中的函数:i2c_adap_s3c_init() subsys_initcall级别
4./driver/i2c/i2c-dev.c中的函数:i2c_dev_init() module_init级别
|
评论暂时关闭