歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> I2C子系統之內核中I2C子系統的結構

I2C子系統之內核中I2C子系統的結構

日期:2017/3/1 10:12:45   编辑:Linux編程

本文開始,分析內核的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. SECTIONS
  7. {
  8. 。。。 。。。
  9. INIT_CALL
  10. CON_INITCALL
  11. SECURITY_INITCALL
  12. 。。。 。。。
  13. }
其中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. #define INIT_CALLS \
  22. VMLINUX_SYMBOL(__initcall_start) = .; \
  23. INITCALLS \
  24. 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. * Early initcalls run before initializing SMP.
  6. *
  7. * Only for built-in code, not modules.
  8. */
  9. #define early_initcall(fn) __define_initcall("early",fn,early)
  10. /*
  11. * A "pure" initcall has no dependencies on anything else, and purely
  12. * initializes variables that couldn't be statically initialized.
  13. *
  14. * This only exists for built-in code, not for modules.
  15. */
  16. #define pure_initcall(fn) __define_initcall("0",fn,0)
  17. #define core_initcall(fn) __define_initcall("1",fn,1)
  18. #define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
  19. #define postcore_initcall(fn) __define_initcall("2",fn,2)
  20. #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
  21. #define arch_initcall(fn) __define_initcall("3",fn,3)
  22. #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
  23. #define subsys_initcall(fn) __define_initcall("4",fn,4)
  24. #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
  25. #define fs_initcall(fn) __define_initcall("5",fn,5)
  26. #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
  27. #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
  28. #define device_initcall(fn) __define_initcall("6",fn,6)
  29. #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
  30. #define late_initcall(fn) __define_initcall("7",fn,7)
  31. #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
  32. #define __initcall(fn) device_initcall(fn)
  33. ... ...
  34. #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. 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級別

Copyright © Linux教程網 All Rights Reserved