1、Linux串口终端初始化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/* 只作基本的初始化,详细的初始化在后面做 */v
2、oid _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
3、 = _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
4、.init)=fninitcall_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的 _att
5、ribute_ 扩展,将其链接入.con_initcall.init 节,方便管理。一个典型的 .con_initcall.init 节的内容为:.Disassembly of section .con_initcall.init:80234f90 :80234f90: 802328e4 lb v1,10468(at) # 这是一个函数指针,指向serial8250_console_init80234f94 : 80234f94: 80232ce4 lb v1,11492(at).因此 console_init() 所做的,就是: console_init() |- tty_register_
6、ldisc() /* Install a line discipline, drivers/char/tty_io.c */ |- serial8250_console_init() - early_uart_console_init()1.1.1 serial8250_console_initserial8250_console_init() 定义于 drivers/serial/8250.cstatic int _init serial8250_console_init(void) serial8250_isa_init_ports(); register_console(&serial8
7、250_console); return 0;console_initcall(serial8250_console_init);static struct uart_8250_port serial8250_portsUART_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 port.line = i; spin_lock_init(&
8、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 port.iobase
9、 = old_serial_porti.port; up-port.irq = irq_canonicalize(old_serial_porti.irq); up-port.uartclk = old_serial_porti.baud_base * 16; up-port.flags = old_serial_porti.flags; up-port.hub6 = old_serial_porti.hub6; up-port.membase = old_serial_porti.iomem_base; up-port.iotype = old_serial_porti.io_type; u
10、p-port.regshift = old_serial_porti.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 /* d
11、efined 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 M
12、OMENCO_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, BAS
13、E_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() 后
14、, 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 = C
15、ON_PRINTBUFFER, .index = -1, .data = &serial8250_reg,;其用来描述一个 serial8250 的 console.这个register_console() 定义于 kernel/printk.c1.1.2 early_uart_console_init()drivers/serial/8250_early.cstatic struct console early_uart_console _initdata = .name = uart, .write = early_uart_write, .setup = early_uart_setup
16、, .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 console1
17、.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.cstatic void _init do_basic_setup(voi
18、d) /* 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_ini
19、t(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(
20、);这些函数主要调用 subsystem_register() 注册一个struct subsystem 结构,进入kobjects.1.2.2 do_initcall()这个于上面 console_init() 类似,其是顺序执行 .initcall.init 节中的所有函数:init/main.cextern initcall_t _initcall_start, _initcall_end;static void _init do_initcalls(void) initcall_t *call; int count = preempt_count(); for (call = _ini
21、tcall_start; call _initcall_end; call+) char *msg = NULL; char msgbuf40; 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, er
22、ror 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);
23、printk(: returned with %sn, 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.in
24、it) *(.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 = .;.
25、链接时,会被替换为实际的地址矣.写入 .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_init
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1