# uname -r
2.6.22.6-g8701f843-dirty
# cat /proc/cmdline
noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
✗ find . -name "*.o"
./s3c2410.o
./built-in.o
./serial_core.o
综述
linux的串口中有几个概念,一个就是文件 /dev/ttyXXX ,一个就是控制台 ,一个就是 early console
0. 串口的寄存器操作
- 收
s3c24xx_serial_rx_chars
rd_regb(port, S3C2410_URXH);
- 发
static void
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
{
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
while (!s3c24xx_serial_console_txrdy(port, ufcon))
barrier();
wr_regb(cons_uart, S3C2410_UTXH, ch);
}
1. 串口设备文件
1.
各种串口设备形成的设备节点/dev/ttyXXX(例如 ttyAMA0,不指代所有的tty*文件) 是串口驱动 通过 uart_register_driver 和 uart_add_one_port 形成的,其中利用了tty子系统和serial子系统.
2.控制台
2.
在 控制台概念中, bootargs 中 要传入 console=aaa console=bbb console=ccc ...(也可能只有console =aaa) , 那么如果 aaa bbb ccc 驱动中有对应的 console 被注册,就是将 printk 的内容打印到 aaa bbb ccc 上,对于上述例子,ccc(最后一个)为交互控制台,其他仅为打印控制台.
在 驱动中,是 用的 console_initcall(s3c24xx_serial_initconsole); 这样子来注册控制台.注册的函数存在于 __con_initcall_start 和 __con_initcall_end 中.
c0021890 T __con_initcall_start
c0021890 t __initcall_kgdb_console_init
c0021894 t __initcall_con_init
c0021898 t __initcall_s3c24xx_serial_initconsole
c002189c T __con_initcall_end
__con_initcall_start 和 __con_initcall_end 中 的函数 在console_init 的时候被调用.
这些函数都调用 register_console 来注册控制台(匹配到 console=xxx 的才能注册成功)
3. early console
3.
早期linux版本(例如linux-2.6.22.6)中 arm架构是没有 early console 的概念的.后来就有了.且发展为两个版本.
early console 作为一种调试手段,在 early_console 注册之后,console_init之前可以利用printk打印到串口上进行调试
early console 仅作为 console_init 之前使用, console_init 之后early console 就被 disabled.
early console 仅实现了发送,没有实现初始化和接收,初始化已经在 bootloader做过.
版本1: early printk early_param("earlyprintk", setup_early_printk);
版本2: 版本1的升级版: earlycon early_param("earlycon", setup_of_earlycon);
3.1 early printk
def_config
CONFIG_EARLY_PRINTK
CONFIG_DEBUG_LL
dts
chosen{
bootargs ="earlyprintk";
};
code
1. 平台无关代码
arch/arm/kernel/early_printk.c
early_param("earlyprintk", setup_early_printk);
early_write
printch
2. 平台相关代码
arch/arm/kernel/debug.S
ENTRY(printch)
addruart_current r3, r1, r2
mov r1, r0
mov r0, #0
b 1b
ENDPROC(printch)
3.2 earlycon
def_config
CONFIG_SERIAL_EARLYCON
CONFIG_OF_EARLY_FLATTREE
CONFIG_SERIAL_SAMSUNG_CONSOLE
dts
chosen{
bootargs = "earlycon";
linux,stdout-path = &uart0;
};
uart0: serial@e2900000 {
compatible = "samsung,s5pv210-uart";
reg = <0xe2900000 0x400>;
};
code
1. 平台无关
early_param("earlycon", setup_of_earlycon);
of_setup_earlycon
register_console(early_console_dev.con);
2. 平台相关
OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart",
s5pv210_early_console_setup);
s5pv210_early_console_setup
samsung_early_console_setup
device->con->write = samsung_early_write;
其他
如果一个串口设备要作为控制台的话,也就是启动信息输出到改串口的话,一定要在串口驱动中 添加 注册 register_console 的代码, 且在 bootargs 中一定要 添加 console=xxx0,xxx 为 register_console中的参数的name成员
内核的串口驱动比较简单,但是在串口驱动之上,封装了很多逻辑.
但是从上层到 printch(操作寄存器的函数) ,这个过程中, 夹杂了很多其他杂七杂八的逻辑
tty子系统
vt
pty
hvc
console
gdb console
dmesg
printk
也就是说 tty console 不只是为 串口驱动准备的,而是 tty console 做一个 生产者, 供给了很多消费者,而串口驱动只是其中一个.