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:

  1. #ifndef __ARMEB__   
  2. jiffies = jiffies_64;  
  3. #else   
  4. jiffies = jiffies_64 + 4;  
  5. #endif   
  6.   
  7. SECTIONS  
  8. {  
  9.                 。。。 。。。  
  10.                 INIT_CALL  
  11.                 CON_INITCALL  
  12.         SECURITY_INITCALL  
  13.                 。。。 。。。  
  14. }  
其中INIT_CALLS为一个宏,定义如下:
  1. #define INITCALLS                                                       \   
  2.     *(.initcallearly.init)                      \  
  3.     VMLINUX_SYMBOL(__early_initcall_end) = .;           \  
  4.     *(.initcall0.init)                      \  
  5.     *(.initcall0s.init)                     \  
  6.     *(.initcall1.init)                      \  
  7.     *(.initcall1s.init)                     \  
  8.     *(.initcall2.init)                      \  
  9.     *(.initcall2s.init)                     \  
  10.     *(.initcall3.init)                      \  
  11.     *(.initcall3s.init)                     \  
  12.     *(.initcall4.init)                      \  
  13.     *(.initcall4s.init)                     \  
  14.     *(.initcall5.init)                      \  
  15.     *(.initcall5s.init)                     \  
  16.     *(.initcallrootfs.init)                     \  
  17.     *(.initcall6.init)                      \  
  18.     *(.initcall6s.init)                     \  
  19.     *(.initcall7.init)                      \  
  20.     *(.initcall7s.init)  
  21.   
  22. #define INIT_CALLS                          \   
  23.         VMLINUX_SYMBOL(__initcall_start) = .;           \  
  24.         INITCALLS                       \  
  25.         VMLINUX_SYMBOL(__initcall_end) = .;  
可以看见存放链接的顺序依次为:.initcall0.init、initcall0s.init、initcall1.init... ...

这只是连接脚本的中的语法,函数中具体是需要宏来给函数做上标记的。

具体在/include/linux/init.h中,相关代码如下:

  1. #define __define_initcall(level,fn,id) \   
  2.     static initcall_t __initcall_##fn##id __used \  
  3.     __attribute__((__section__(".initcall" level ".init"))) = fn  
  4.   
  5. /* 
  6.  * Early initcalls run before initializing SMP. 
  7.  * 
  8.  * Only for built-in code, not modules. 
  9.  */  
  10. #define early_initcall(fn)      __define_initcall("early",fn,early)   
  11.   
  12. /* 
  13.  * A "pure" initcall has no dependencies on anything else, and purely 
  14.  * initializes variables that couldn't be statically initialized. 
  15.  * 
  16.  * This only exists for built-in code, not for modules. 
  17.  */  
  18. #define pure_initcall(fn)       __define_initcall("0",fn,0)   
  19.   
  20. #define core_initcall(fn)       __define_initcall("1",fn,1)   
  21. #define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)   
  22. #define postcore_initcall(fn)       __define_initcall("2",fn,2)   
  23. #define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)   
  24. #define arch_initcall(fn)       __define_initcall("3",fn,3)   
  25. #define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)   
  26. #define subsys_initcall(fn)     __define_initcall("4",fn,4)   
  27. #define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)   
  28. #define fs_initcall(fn)         __define_initcall("5",fn,5)   
  29. #define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)   
  30. #define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)   
  31. #define device_initcall(fn)     __define_initcall("6",fn,6)   
  32. #define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)   
  33. #define late_initcall(fn)       __define_initcall("7",fn,7)   
  34. #define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)   
  35.   
  36. #define __initcall(fn) device_initcall(fn)   
  37.   
  38. ... ...  
  39. #define module_init(x)    __initcall(x);   
这里就可以发现,在实际的驱动中,初始化函数都有如下形式:module_init(init_func)

原来module_init就是一个宏,可以发现module_init宏最终展开后会将init_func函数做个

initcall6.init的标记,最终此函数就被链接在此initcall6.init的位置,而那些序号大initcall6.init的函数

则存放在被module_init修饰的函数之前的位置。

Makefile文件的控制

i2c子系统的相关源码集中在/driver/i2c目录下

所以此处只从/driver/i2c目录下的Makefile文件分析

/driver/i2c/Makefile:
  1. obj-$(CONFIG_I2C_BOARDINFO)     += i2c-boardinfo.o  
  2. obj-$(CONFIG_I2C)               += i2c-core.o  
  3. obj-$(CONFIG_I2C_SMBUS)         += i2c-smbus.o  
  4. obj-$(CONFIG_I2C_CHARDEV)       += i2c-dev.o  
  5. obj-$(CONFIG_I2C_MUX)           += i2c-mux.o  
  6. obj-y                           += algos/ busses/ muxes/  
  7.   
  8. ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG  
可见链接的顺序是i2c.-boardinfo、i2c-core、i2c-dev... ...

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级别

  • 1
  • 2
  • 下一页

相关内容