二、设备和驱动的注册时序


一、引言

      在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_PCI 29 ret = pci_register_driver(&serial_txx9_pci_driver); 30 #endif 31 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 #endif 101 } 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_TEXT 109 SCHED_TEXT 110 LOCK_TEXT 111 KPROBES_TEXT 112 #ifdef CONFIG_MMU 113 *(.fixup) 114 #endif 115 *(.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_UNWIND 128 /* 129 * Stack unwinding tables 130 */ 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 #endif 143 144 #ifdef CONFIG_XIP_KERNEL 145 __data_loc = ALIGN(4); /* location in binary */ 146 . = PAGE_OFFSET + TEXT_OFFSET; 147 #else 148 . = ALIGN(THREAD_SIZE); 149 __data_loc = .; 150 #endif 151 152 .data : AT(__data_loc) { 153 _data = .; /* address in memory */ 154 _sdata = .; 155 156 /* 157 * first, the init task union, aligned 158 * to an 8192 byte boundary. 159 */ 160 *(.data.init_task) 161 162 #ifdef CONFIG_XIP_KERNEL 163 . = ALIGN(PAGE_SIZE); 164 __init_begin = .; 165 INIT_DATA 166 . = ALIGN(PAGE_SIZE); 167 __init_end = .; 168 #endif 169 170 . = ALIGN(PAGE_SIZE); 171 __nosave_begin = .; 172 *(.data.nosave) 173 . = ALIGN(PAGE_SIZE); 174 __nosave_end = .; 175 176 /* 177 * then the cacheline aligned data 178 */ 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_MMU 188 *(__ex_table) 189 #endif 190 __stop___ex_table = .; 191 192 /* 193 * and the usual data section 194 */ 195 DATA_DATA 196 CONSTRUCTORS 197 198 _edata = .; 199 } 200 _edata_loc = __data_loc + SIZEOF(.data); 201 202 #ifdef CONFIG_HAVE_TCM 203 /* 204 * We align everything to a page boundary so we can 205 * free it after init has commenced and TCM contents have 206 * 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 RAM 216 * Put VMA to the TCM address and LMA to the common RAM 217 * and we'll upload the contents from RAM to TCM and free 218 * 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 the 231 * 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 #endif 258 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 empty 278 * If you have to comment these two assert statements out, your 279 * 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

 

 

 

 

 

 

 

 

 

 

 

相关内容

    暂无相关文章