分类: LINUX
2011-03-26 14:27:40
1. 串口初始化过程 start_kernel() |----- ... |----- setup_arch() |----- ... |----- build_all_zonelists() |----- page_alloc_init() |----- ... |----- trap_init() |----- ... |----- console_init() |----- ... |----- mem_init() |----- ... `----- rest_init() ---> kernel_thread() --> init() -->do_basic_setup() 1.1 console_init() [drivers/char/tty_io.c] /* 只作基本的初始化,详细的初始化在后面做 */ void __init console_init(void) { initcall_t *call; /* Setup the default TTY line discipline. */ (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); /* * set up the console device so that later boot sequences can * inform about problems etc.. */ #ifdef CONFIG_EARLY_PRINTK disable_early_printk(); #endif call = __con_initcall_start; while (call < __con_initcall_end) { (*call)(); call++; } } 然后执行依次执行 .con_initcall.init 节中的函数,该节的每项为一个函数指针,使用宏 console_initcall(FUNC_NAME) 将函数指针填入,该宏定义于 [include/linux/init.h]: #define console_initcall(fn) \ static initcall_t __initcall_##fn \ __attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn initcall_t 为一函数指针: typedef int (*initcall_t)(void); 如: console_initcall(serial8250_console_init) 则展开为: static initcall_t __initcall_serial8250_console_init = __attribute_used__ \ __attribute__((__section__(".con_initcall.init"))) = serial8250_console_init; 即定义一个函数指针,使其指向 serial8250_console_init,并使用gcc的 __attribute__ 扩展,将其链接入.con_initcall.init 节,方便管理。 一个典型的 .con_initcall.init 节的内容为: ... Disassembly of section .con_initcall.init: 80234f90 <__initcall_serial8250_console_init>: 80234f90: 802328e4 lb v1,10468(at) # 这是一个函数指针,指向serial8250_console_init 80234f94 <__initcall_early_uart_console_init>: 80234f94: 80232ce4 lb v1,11492(at) ... 因此 console_init() 所做的,就是: console_init() |----- tty_register_ldisc() /* Install a line discipline, [drivers/char/tty_io.c] */ |----- serial8250_console_init() `----- early_uart_console_init() 1.1.1 serial8250_console_init serial8250_console_init() 定义于 [drivers/serial/8250.c] static int __init serial8250_console_init(void) { serial8250_isa_init_ports(); register_console(&serial8250_console); return 0; } console_initcall(serial8250_console_init); static struct uart_8250_port serial8250_ports[UART_NR]; static void __init serial8250_isa_init_ports(void) { struct uart_8250_port *up; static int first = 1; int i; if (!first) return; first = 0; for (i = 0; i < nr_uarts; i++) { struct uart_8250_port *up = &serial8250_ports[i]; up->port.line = i; spin_lock_init(&up->port.lock); init_timer(&up->timer); up->timer.function = serial8250_timeout; /* * ALPHA_KLUDGE_MCR needs to be killed. */ up->mcr_mask = ~ALPHA_KLUDGE_MCR; up->mcr_force = ALPHA_KLUDGE_MCR; up->port.ops = &serial8250_pops; } for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++, up++) { up->port.iobase = old_serial_port[i].port; up->port.irq = irq_canonicalize(old_serial_port[i].irq); up->port.uartclk = old_serial_port[i].baud_base * 16; up->port.flags = old_serial_port[i].flags; up->port.hub6 = old_serial_port[i].hub6; up->port.membase = old_serial_port[i].iomem_base; up->port.iotype = old_serial_port[i].io_type; up->port.regshift = old_serial_port[i].iomem_reg_shift; if (share_irqs) up->port.flags |= UPF_SHARE_IRQ; } } serial8250_isa_init_ports() 所做的事即使用 old_serial_port 来初始化 struct uart_8250_port 结构数组 serial8250_ports. 这个 old_serial_port 定义为: static const struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */ }; [include/asm-mips/serial.h] #define SERIAL_PORT_DFNS \ DDB5477_SERIAL_PORT_DEFNS \ EV64120_SERIAL_PORT_DEFNS \ IP32_SERIAL_PORT_DEFNS \ JAZZ_SERIAL_PORT_DEFNS \ STD_SERIAL_PORT_DEFNS \ MOMENCO_OCELOT_G_SERIAL_PORT_DEFNS \ MOMENCO_OCELOT_C_SERIAL_PORT_DEFNS \ MOMENCO_OCELOT_SERIAL_PORT_DEFNS \ MOMENCO_OCELOT_3_SERIAL_PORT_DEFNS \ BCM947XX_SERIAL_PORT_DEFNS \ BCM56218_SERIAL_PORT_DEFNS 这个根据具体的平台配置,使用相应的宏定义. 如当 CONFIG_HAVE_STD_PC_SERIAL_PORT 时: #ifdef CONFIG_HAVE_STD_PC_SERIAL_PORT #define STD_SERIAL_PORT_DEFNS \ /* UART CLK PORT IRQ FLAGS */ \ { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ #else /* CONFIG_HAVE_STD_PC_SERIAL_PORTS */ #define STD_SERIAL_PORT_DEFNS #endif /* CONFIG_HAVE_STD_PC_SERIAL_PORTS */ 否则为空宏 serial8250_isa_init_ports() 后, serial8250_console_init() 调用 register_console(&serial8250_console) 注册一个struct console 结构: static struct uart_driver serial8250_reg; static struct console serial8250_console = { .name = "ttyS", .write = serial8250_console_write, .device = uart_console_device, .setup = serial8250_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &serial8250_reg, }; 其用来描述一个 serial8250 的 console. 这个register_console() 定义于 [kernel/printk.c] 1.1.2 early_uart_console_init() [drivers/serial/8250_early.c] static struct console early_uart_console __initdata = { .name = "uart", .write = early_uart_write, .setup = early_uart_setup, .flags = CON_PRINTBUFFER, .index = -1, }; static int __init early_uart_console_init(void) { if (!early_uart_registered) { register_console(&early_uart_console); early_uart_registered = 1; } return 0; } console_initcall(early_uart_console_init); 和 serial8250_console_init() 类似,也是注册一个 console 结构,表示一个 uart console 1.2 rest_init() rest_init() |----- ... |----- smp_prepare_cpus(max_cpus) |----- do_pre_smp_initcalls() |----- smp_init() |----- sched_init_smp() |----- cpuset_init_smp() |----- do_basic_setup() |----- ... `----- init_post() 1.2.1 do_basic_setup() 到 do_basic_setup() 时,与体系结构相关的部分已经初始化完了,现在开始初始化设备了: [init/main.c] static void __init do_basic_setup(void) { /* drivers will send hotplug events */ init_workqueues(); usermodehelper_init(); driver_init(); /* initialize driver model */ init_irq_proc(); do_initcalls(); /* 顺序执行 .initcall.init 节中的所有函数 */ } 1.2.1 driver_init() driver_init() 定义于 [drivers/base/init.c] 主要完成 driver subsystem 的初始化: void __init driver_init(void) { /* These are the core pieces */ devices_init(); buses_init(); classes_init(); firmware_init(); hypervisor_init(); /* These are also core pieces, but must come after the * core core pieces. */ platform_bus_init(); system_bus_init(); cpu_dev_init(); memory_dev_init(); attribute_container_init(); } 这些函数主要调用 subsystem_register() 注册一个struct subsystem 结构,进入kobjects. 1.2.2 do_initcall() 这个于上面 console_init() 类似,其是顺序执行 .initcall.init 节中的所有函数: [init/main.c] extern initcall_t __initcall_start[], __initcall_end[]; static void __init do_initcalls(void) { initcall_t *call; int count = preempt_count(); for (call = __initcall_start; call < __initcall_end; call++) { char *msg = NULL; char msgbuf[40]; int result; if (initcall_debug) { printk("Calling initcall 0x%p", *call); print_fn_descriptor_symbol(": %s()", (unsigned long) *call); printk("\n"); } result = (*call)(); if (result && result != -ENODEV && initcall_debug) { sprintf(msgbuf, "error code %d", result); msg = msgbuf; } if (preempt_count() != count) { msg = "preemption imbalance"; preempt_count() = count; } if (irqs_disabled()) { msg = "disabled interrupts"; local_irq_enable(); } if (msg) { printk(KERN_WARNING "initcall at 0x%p", *call); print_fn_descriptor_symbol(": %s()", (unsigned long) *call); printk(": returned with %s\n", msg); } } /* Make sure there is no pending stuff from the initcall sequence */ flush_scheduled_work(); } 关于符号地址 __initcall_start, __initcall_end 的来源,则是由编译系统写在 [arch/mips/kernel/vmlinux.lds]中: ...... __initcall_start = .; .initcall.init : { *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) } __initcall_end = .; ...... 链接时,会被替换为实际的地址矣. 写入 .initcall.init 节的函数指针,有一组辅助的宏定义于[include/linux/init.h]: #define pure_initcall(fn) __define_initcall("0",fn,1) #define core_initcall(fn) __define_initcall("1",fn,1) #define core_initcall_sync(fn) __define_initcall("1s",fn,1s) #define postcore_initcall(fn) __define_initcall("2",fn,2) #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s) #define arch_initcall(fn) __define_initcall("3",fn,3) #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s) #define subsys_initcall(fn) __define_initcall("4",fn,4) #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s) #define fs_initcall(fn) __define_initcall("5",fn,5) #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s) #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs) #define device_initcall(fn) __define_initcall("6",fn,6) #define device_initcall_sync(fn) __define_initcall("6s",fn,6s) #define late_initcall(fn) __define_initcall("7",fn,7) #define late_initcall_sync(fn) __define_initcall("7s",fn,7s) 其优先级依次降低,优先级越高的,越靠前,则先被执行. 在这个节的最后,可以看到调用串口相关的初始化函数: Disassembly of section .initcall.init: ...... ...... 80234f80 <__initcall_serial8250_init6>: 80234f80: 80232910 lb v1,10512(at) 80234f84 <__initcall_random32_reseed7>: 80234f84: 802319c4 lb v1,6596(at) 80234f88 <__initcall_seqgen_init7>: 80234f88: 80231ae8 lb v1,6888(at) 80234f8c <__initcall_early_uart_console_switch7>: 80234f8c: 80233140 lb v1,12608(at) 因此: do_basic_setup() |----- ... |----- driver_init() |----- init_irq_proc() |----- do_initcalls() |----- ... |----- ... |----- serial8250_init() |----- seqgen_init() `----- early_uart_console_switch() |----- ... `----- ... 1.2.2.1 serial8250_init() [drivers/serial/8250.c] static int __init serial8250_init(void) { int ret, i; if (nr_uarts > UART_NR) nr_uarts = UART_NR; printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ " "%d ports, IRQ sharing %sabled\n", nr_uarts, share_irqs ? "en" : "dis"); for (i = 0; i < NR_IRQS; i++) spin_lock_init(&irq_lists[i].lock); ret = uart_register_driver(&serial8250_reg); if (ret) goto out; serial8250_isa_devs = platform_device_alloc("serial8250", PLAT8250_DEV_LEGACY); if (!serial8250_isa_devs) { ret = -ENOMEM; goto unreg_uart_drv; } ret = platform_device_add(serial8250_isa_devs); if (ret) goto put_dev; serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); ret = platform_driver_register(&serial8250_isa_driver); ---> 注册时调用 serial8250_probe() if (ret == 0) goto out; platform_device_del(serial8250_isa_devs); put_dev: platform_device_put(serial8250_isa_devs); unreg_uart_drv: uart_unregister_driver(&serial8250_reg); out: return ret; } 注意 platform_driver_register() 中,注册时调用 serial8250_probe(), 从[arch/mips/emma3p/et10068/platform.c] 中设置的 struct platform_device 结构数组中获得板极相关的串口设备. 1.2.2.2 early_uart_console_switch() [drivers/serial/8250_early.c] static int __init early_uart_console_switch(void) { struct early_uart_device *device = &early_device; struct uart_port *port = &device->port; int mmio, line; if (!(early_uart_console.flags & CON_ENABLED)) return 0; /* Try to start the normal driver on a matching line. */ mmio = (port->iotype == UPIO_MEM); line = serial8250_start_console(port, device->options); /* start console */ if (line < 0) printk("No ttyS device at %s 0x%lx for console\n", mmio ? "MMIO" : "I/O port", mmio ? port->mapbase : (unsigned long) port->iobase); unregister_console(&early_uart_console); if (mmio) iounmap(port->membase); return 0; } late_initcall(early_uart_console_switch); 到此串口终端正式可用矣~~~ 2. 兼容 8250 的串口控制器驱动位于: drivers/serial/8250_early.c drivers/serial/8250.c drivers/serial/serial_core.c |