|
在/init/main.c中,start_kernel调用了console_init( ) ,该函数完成了串口的初始化、将串口向console注册等功能.
在linux/drivers/char/tty_io.c里,有console_init的函数定义.我开始一直在kernel或者linux的文件夹里面找,但是没找到,很奇怪地在char的驱动部分找到了这个函数的定义.虽然觉得可以解释的通,但是串口tty_io必须在char的设备里面吗?在block和net里面却没有同类的问题,恕我孤陋寡闻啊......继续说,里面有两个函数,一个是tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); ,一个是call.对于call的定义很奇怪: #ifdef CONFIG_SERIAL_68360 /* This is not a console initcall. I know not what it's doing here. So I haven't moved it. dwmw2 */ rs_360_init(); #endif call = &__con_initcall_start; while (call < &__con_initcall_end) { (*call)(); call++;
在/arch/armnommu/kernel/vmlinux.lds.S中连接脚本汇编中有这段代码 __initcall_start = .; *(.initcall1.init) *(.initcall2.init) *(.initcall3.init) *(.initcall4.init) *(.initcall5.init) *(.initcall6.init) *(.initcall7.init) __initcall_end = .;
在/include/linux/init.h里面,将上述两者联系起来: #define console_initcall(fn) / static initcall_t __initcall_##fn / __attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn 那最终是如何把具体的对串口的函数和console_init关联起来的,在/drivers/serial/8250.c里面有这么一句话: console_initcall(serial8250_console_init); 这就让初始化文件调用的是serial8250_console_init.
接下来进入8250的部分.为什么是8250呢,因为在make config的时候,我们选了8250和16550的兼容设备,前者是个很通用的串口芯片,后者是接口芯片规范.
在8250.c里面定义了串口的个数和串口的结构: static struct uart_8250_port serial8250_ports[UART_NR]; struct uart_8250_port { struct uart_portport; struct timer_listtimer;/* "no irq" timer */ struct list_headlist;/* ports on this IRQ */ unsigned intcapabilities;/* port capabilities */ unsigned shortrev; unsigned characr; unsigned charier; unsigned charlcr; unsigned charmcr_mask;/* mask of user bits */ unsigned charmcr_force;/* mask of forced bits */ unsigned charlsr_break_flag;
/* * We provide a per-port pm hook. */ void(*pm)(struct uart_port *port, unsigned int state, unsigned int old); };
在初始化函数中,将各个端口的各项属性赋值,这里指的属性是硬件地址等属性,当只有一个端口的时候循环只执行一次.然后是注册串口,在/kernel/printk.c里面,我本以为printk只是在上层输出的,原来它这个文件里面包含了如此之多的底层操作的函数.
在console这个结构里面,有一个setup的成员是一个函数,该函数在8250.c里面被调用,是设置串口的属性,这里指的属性是数据帧格式的属性.最终的一些寄存器的设置还是不在这里,在/drivers/char/serial_core.c里面,这里有很多uart的函数,包括串口速率和时钟的换算.速率和时钟在lpc22xx.h和time.h里面有过定义,在serial_core里面的前面还定义了set_termios=uart_set_termios,这个其实就是在8250.c里面的set_termios=serial8250_set_termios,最终又调用到serial_core里面的uart_get_divisor函数中去.
看起来很复杂,其实它这个抽象工作做的非常好,把串口抽象成uart,把uart具体成8250,上层和下次分的很清楚,各自负责各自的工作.把tty抽象成console,通过对结构和地址的引用来实现其控制台,不失通用性也不失具体性. | |