首页 > 学院 > 操作系统 > 正文

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

2024-06-28 13:26:24
字体:
来源:转载
供稿:网友
二、设备和驱动的注册时序转载请注明转自:http://www.CUOXin.com/connectfuture/一、引言

在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


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表