在platform驱动程序框架中,我们了解到,platform设备和驱动最终会挂载在platform总线上,platform总线会对设备和驱动进行匹配。那么设备和驱动是怎么注册到platform框架中去的,其先后顺序又是怎样的?
二、设备和驱动注册platform_device_register注册设备
1 /** 2 * platform_device_register - add a platform-level device 3 * @pdev: platform device we're adding 4 */ 5 int platform_device_register(struct platform_device *pdev) 6 { 7 device_initialize(&pdev->dev); 8 return platform_device_add(pdev); 9 }10 EXPORT_SYMBOL_GPL(platform_device_register);platform_device_register
platform_driver_register注册驱动
1 /** 2 * platform_driver_register 3 * @drv: platform driver structure 4 */ 5 int platform_driver_register(struct platform_driver *drv) 6 { 7 drv->driver.bus = &platform_bus_type; 8 if (drv->PRobe) 9 drv->driver.probe = platform_drv_probe;10 if (drv->remove)11 drv->driver.remove = platform_drv_remove;12 if (drv->shutdown)13 drv->driver.shutdown = platform_drv_shutdown;14 15 return driver_register(&drv->driver);16 }17 EXPORT_SYMBOL_GPL(platform_driver_register);platform_driver_register
设备注册sample
1 static struct plat_serial8250_port serial_platform_data[] = { 2 { 3 .membase = OMAP1_IO_ADDRESS(OMAP_UART1_BASE), 4 .mapbase = OMAP_UART1_BASE, 5 .irq = INT_UART1, 6 .flags = UPF_BOOT_AUTOCONF, 7 .iotype = UPIO_MEM, 8 .regshift = 2, 9 .uartclk = OMAP16XX_BASE_BAUD * 16,10 },11 {12 .membase = OMAP1_IO_ADDRESS(OMAP_UART2_BASE),13 .mapbase = OMAP_UART2_BASE,14 .irq = INT_UART2,15 .flags = UPF_BOOT_AUTOCONF,16 .iotype = UPIO_MEM,17 .regshift = 2,18 .uartclk = OMAP16XX_BASE_BAUD * 16,19 },20 {21 .membase = OMAP1_IO_ADDRESS(OMAP_UART3_BASE),22 .mapbase = OMAP_UART3_BASE,23 .irq = INT_UART3,24 .flags = UPF_BOOT_AUTOCONF,25 .iotype = UPIO_MEM,26 .regshift = 2,27 .uartclk = OMAP16XX_BASE_BAUD * 16,28 },29 { },30 };31 32 static struct platform_device serial_device = {33 .name = "serial8250",34 .id = PLAT8250_DEV_PLATFORM,35 .dev = {36 .platform_data = serial_platform_data,37 },38 };39 40 static int __init omap_init(void)41 {42 return platform_device_register(&serial_device);43 }44 arch_initcall(omap_init);device register sample
驱动注册sample
1 static int __init serial_txx9_init(void) 2 { 3 int ret; 4 5 printk(KERN_INFO "%s version %s/n", serial_name, serial_version); 6 7 ret = uart_register_driver(&serial_txx9_reg); 8 if (ret) 9 goto out;10 11 serial_txx9_plat_devs = platform_device_alloc("serial_txx9", -1);12 if (!serial_txx9_plat_devs) {13 ret = -ENOMEM;14 goto unreg_uart_drv;15 }16 17 ret = platform_device_add(serial_txx9_plat_devs);18 if (ret)19 goto put_dev;20 21 serial_txx9_register_ports(&serial_txx9_reg,22 &serial_txx9_plat_devs->dev);23 24 ret = platform_driver_register(&serial_txx9_plat_driver);25 if (ret)26 goto del_dev;27 28 #ifdef ENABLE_SERIAL_TXX9_PCI29 ret = pci_register_driver(&serial_txx9_pci_driver);30 #endif31 if (ret == 0)32 goto out;33 34 del_dev:35 platform_device_del(serial_txx9_plat_devs);36 put_dev:37 platform_device_put(serial_txx9_plat_devs);38 unreg_uart_drv:39 uart_unregister_driver(&serial_txx9_reg);40 out:41 return ret;42 }43 44 module_init(serial_txx9_init);driver register sample
设备注册时定义了该设备用到的一些硬件资源,如中断线,内存地址范围,稳压电源等。
驱动注册时定义了probe、remove等函数,在probe阶段,会做一些初始化动作,如将中断处理函数关联到设备的中断线上。
由此看来,先注册设备再注册驱动。
三、init函数调用时序device初始化用到了宏arch_initcall,driver初始化用到了宏module_init。
在include/linux/init.h中定义了arch_initcall,其根据level标示,在load阶段会按顺序加载。
1 /* initcalls are now grouped by functionality into separate 2 * subsections. Ordering inside the subsections is determined 3 * by link order. 4 * For backwards compatibility, initcall() puts the call in 5 * the device init subsection. 6 * 7 * The `id' arg to __define_initcall() is needed so that multiple initcalls 8 * can point at the same handler without causing duplicate-symbol build errors. 9 */10 11 #define __define_initcall(level,fn,id) /12 static initcall_t __initcall_##fn##id __used /13 __attribute__((__section__(".initcall" level ".init"))) = fn__define_initcall
arch_initcall的调用顺序是3。
1 #define core_initcall(fn) __define_initcall("1",fn,1) 2 #define core_initcall_sync(fn) __define_initcall("1s",fn,1s) 3 #define postcore_initcall(fn) __define_initcall("2",fn,2) 4 #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s) 5 #define arch_initcall(fn) __define_initcall("3",fn,3) 6 #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s) 7 #define subsys_initcall(fn) __define_initcall("4",fn,4) 8 #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s) 9 #define fs_initcall(fn) __define_initcall("5",fn,5)10 #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)11 #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)12 #define device_initcall(fn) __define_initcall("6",fn,6)13 #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)14 #define late_initcall(fn) __define_initcall("7",fn,7)15 #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)arch_initcall
arch_initcall和module_init宏最终都会扩展成__define_initcall,将函数装载到__initcall段中,其根据level在此段中排序。
vmlinux的load脚本,其中有initcall段定义。
1 /* ld script to make ARM Linux kernel 2 * taken from the i386 version by Russell King 3 * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz> 4 */ 5 6 #include <asm-generic/vmlinux.lds.h> 7 #include <asm/thread_info.h> 8 #include <asm/memory.h> 9 #include <asm/page.h> 10 11 OUTPUT_ARCH(arm) 12 ENTRY(stext) 13 14 #ifndef __ARMEB__ 15 jiffies = jiffies_64; 16 #else 17 jiffies = jiffies_64 + 4; 18 #endif 19 20 SECTIONS 21 { 22 #ifdef CONFIG_XIP_KERNEL 23 . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR); 24 #else 25 . = PAGE_OFFSET + TEXT_OFFSET; 26 #endif 27 .text.head : { 28 _stext = .; 29 _sinittext = .; 30 *(.text.head) 31 } 32 33 .init : { /* Init code and data */ 34 INIT_TEXT 35 _einittext = .; 36 __proc_info_begin = .; 37 *(.proc.info.init) 38 __proc_info_end = .; 39 __arch_info_begin = .; 40 *(.arch.info.init) 41 __arch_info_end = .; 42 __tagtable_begin = .; 43 *(.taglist.init) 44 __tagtable_end = .; 45 . = ALIGN(16); 46 __setup_start = .; 47 *(.init.setup) 48 __setup_end = .; 49 __early_begin = .; 50 *(.early_param.init) 51 __early_end = .; 52 __initcall_start = .; 53 INITCALLS 54 __initcall_end = .; 55 __con_initcall_start = .; 56 *(.con_initcall.init) 57 __con_initcall_end = .; 58 __security_initcall_start = .; 59 *(.security_initcall.init) 60 __security_initcall_end = .; 61 #ifdef CONFIG_BLK_DEV_INITRD 62 . = ALIGN(32); 63 __initramfs_start = .; 64 usr/built-in.o(.init.ramfs) 65 __initramfs_end = .; 66 #endif 67 . = ALIGN(PAGE_SIZE); 68 __per_cpu_load = .; 69 __per_cpu_start = .; 70 *(.data.percpu.page_aligned) 71 *(.data.percpu) 72 *(.data.percpu.shared_aligned) 73 __per_cpu_end = .; 74 #ifndef CONFIG_XIP_KERNEL 75 __init_begin = _stext; 76 INIT_DATA 77 . = ALIGN(PAGE_SIZE); 78 __init_end = .; 79 #endif 80 } 81 82 /DISCARD/ : { /* Exit code and data */ 83 EXIT_TEXT 84 EXIT_DATA 85 *(.exitcall.exit) 86 *(.discard) 87 *(.ARM.exidx.exit.text) 88 *(.ARM.extab.exit.text) 89 #ifndef CONFIG_HOTPLUG_CPU 90 *(.ARM.exidx.cpuexit.text) 91 *(.ARM.extab.cpuexit.text) 92 #endif 93 #ifndef CONFIG_HOTPLUG 94 *(.ARM.exidx.devexit.text) 95 *(.ARM.extab.devexit.text) 96 #endif 97 #ifndef CONFIG_MMU 98 *(.fixup) 99 *(__ex_table)100 #endif101 }102 103 .text : { /* Real text segment */104 _text = .; /* Text and read-only data */105 __exception_text_start = .;106 *(.exception.text)107 __exception_text_end = .;108 TEXT_TEXT109 SCHED_TEXT110 LOCK_TEXT111 KPROBES_TEXT112 #ifdef CONFIG_MMU113 *(.fixup)114 #endif115 *(.gnu.warning)116 *(.rodata)117 *(.rodata.*)118 *(.glue_7)119 *(.glue_7t)120 *(.got) /* Global offset table */121 }122 123 RO_DATA(PAGE_SIZE)124 125 _etext = .; /* End of text and rodata section */126 127 #ifdef CONFIG_ARM_UNWIND128 /*129 * Stack unwinding tables130 */131 . = ALIGN(8);132 .ARM.unwind_idx : {133 __start_unwind_idx = .;134 *(.ARM.exidx*)135 __stop_unwind_idx = .;136 }137 .ARM.unwind_tab : {138 __start_unwind_tab = .;139 *(.ARM.extab*)140 __stop_unwind_tab = .;141 }142 #endif143 144 #ifdef CONFIG_XIP_KERNEL145 __data_loc = ALIGN(4); /* location in binary */146 . = PAGE_OFFSET + TEXT_OFFSET;147 #else148 . = ALIGN(THREAD_SIZE);149 __data_loc = .;150 #endif151 152 .data : AT(__data_loc) {153 _data = .; /* address in memory */154 _sdata = .;155 156 /*157 * first, the init task union, aligned158 * to an 8192 byte boundary.159 */160 *(.data.init_task)161 162 #ifdef CONFIG_XIP_KERNEL163 . = ALIGN(PAGE_SIZE);164 __init_begin = .;165 INIT_DATA166 . = ALIGN(PAGE_SIZE);167 __init_end = .;168 #endif169 170 . = ALIGN(PAGE_SIZE);171 __nosave_begin = .;172 *(.data.nosave)173 . = ALIGN(PAGE_SIZE);174 __nosave_end = .;175 176 /*177 * then the cacheline aligned data178 */179 . = ALIGN(32);180 *(.data.cacheline_aligned)181 182 /*183 * The exception fixup table (might need resorting at runtime)184 */185 . = ALIGN(32);186 __start___ex_table = .;187 #ifdef CONFIG_MMU188 *(__ex_table)189 #endif190 __stop___ex_table = .;191 192 /*193 * and the usual data section194 */195 DATA_DATA196 CONSTRUCTORS197 198 _edata = .;199 }200 _edata_loc = __data_loc + SIZEOF(.data);201 202 #ifdef CONFIG_HAVE_TCM203 /*204 * We align everything to a page boundary so we can205 * free it after init has commenced and TCM contents have206 * been copied to its destination.207 */208 .tcm_start : {209 . = ALIGN(PAGE_SIZE);210 __tcm_start = .;211 __itcm_start = .;212 }213 214 /*215 * Link these to the ITCM RAM216 * Put VMA to the TCM address and LMA to the common RAM217 * and we'll upload the contents from RAM to TCM and free218 * the used RAM after that.219 */220 .text_itcm ITCM_OFFSET : AT(__itcm_start)221 {222 __sitcm_text = .;223 *(.tcm.text)224 *(.tcm.rodata)225 . = ALIGN(4);226 __eitcm_text = .;227 }228 229 /*230 * Reset the dot pointer, this is needed to create the231 * relative __dtcm_start below (to be used as extern in code).232 */233 . = ADDR(.tcm_start) + SIZEOF(.tcm_start) + SIZEOF(.text_itcm);234 235 .dtcm_start : {236 __dtcm_start = .;237 }238 239 /* TODO: add remainder of ITCM as well, that can be used for data! */240 .data_dtcm DTCM_OFFSET : AT(__dtcm_start)241 {242 . = ALIGN(4);243 __sdtcm_data = .;244 *(.tcm.data)245 . = ALIGN(4);246 __edtcm_data = .;247 }248 249 /* Reset the dot pointer or the linker gets confused */250 . = ADDR(.dtcm_start) + SIZEOF(.data_dtcm);251 252 /* End marker for freeing TCM copy in linked object */253 .tcm_end : AT(ADDR(.dtcm_start) + SIZEOF(.data_dtcm)){254 . = ALIGN(PAGE_SIZE);255 __tcm_end = .;256 }257 #endif258 259 .bss : {260 __bss_start = .; /* BSS */261 *(.bss)262 *(COMMON)263 __bss_stop = .;264 _end = .;265 }266 /* Stabs debugging sections. */267 .stab 0 : { *(.stab) }268 .stabstr 0 : { *(.stabstr) }269 .stab.excl 0 : { *(.stab.excl) }270 .stab.exclstr 0 : { *(.stab.exclstr) }271 .stab.index 0 : { *(.stab.index) }272 .stab.indexstr 0 : { *(.stab.indexstr) }273 .comment 0 : { *(.comment) }274 }275 276 /*277 * These must never be empty278 * If you have to comment these two assert statements out, your279 * binutils is too old (for other reasons as well)280 */281 ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")282 ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")vmlinux.lds
在do_initcalls中会从initcall段的函数地址开始,依次调用此段中的函数,设备和驱动的初始化即在此被调用。
调用顺序:
start_kernel->reset_init->kernel_init->do_basic_setup->do_initcalls
1 extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[]; 2 3 static void __init do_initcalls(void) 4 { 5 initcall_t *call; 6 7 for (call = __early_initcall_end; call < __initcall_end; call++) 8 do_one_initcall(*call); 9 10 /* Make sure there is no pending stuff from the initcall sequence */11 flush_scheduled_work();12 }do_initcalls
新闻热点
疑难解答