origin: http://blog.chinaunix.net/uid-25984886-id-3029881.html
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_init
6>:
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_switch
7>:
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