console=ttySAC0,115200
这是bootargs 里最常见的一句。它被用来选择从哪个设备输出内核、驱动的printk信息。
这究竟是什么意思?
串行端口终端(/dev/ttySn)
计算机把每个串行端口都看作是一个字符设备。这些串行端口所对应的设备名称是/dev/ttyS0,/dev/ttyS1等,设备号分别是(4,0), (4,1)等.
PC上的串口一般是ttySn,板子上Linux的串口一般叫做ttySACn.
控制终端(/dev/tty)
如果当前进程有控制终端(Controlling Terminal)的话,那么/dev/tty就是当前进程的控制终端的设备特殊文件。对于你登录的shell,/dev/tty就是你使用的终端,设备号是(5,0)。不同的程序打开这个设备文件可能指向的终端不同。
控制台终端(/dev/ttyn, /dev/console)
tty1–tty6等 称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名current tty,系统所产生的信息会发送到该终端上(这时也叫控制台终端)。
/dev/console即控制台,是与操作系统交互的设备,系统将一些信息直接输出到控制台上。
Linux内核中,有个console设备对象,它会关联到某个具体的设备,例如串口或者FrameBuffer,当printk有消息打印打印到console中时,console会调用关联的具体设备的fops.write,将信息输出到具体设备上。
如果是串口,那么就会在串口上出现发送的波形,如果是framebuffer,那么就会在LCD上显示出来。
console=ttySAC0,就是将板子上的具体设备,/dev/ttySAC0,关联到console设备。
当console有数据输出时,会调用ttySAC0.fops.write发送。
那么这个ttySAC0又是怎么来的呢?
这就涉及到linux的TTY子系统了。
TT驱动架构,也是一个分层的架构,包含了TTYCORE,TTYROUTINE,TTYDRIVER等。
TTYCORE在tty_io.c文件中实现,被实现为一个CDEV,它对用户程序提供CDEV的访问机制,即fops,对下层,又需要调用tty_driver中的函数接口。
这样,tty设备驱动的工作,就转化为填充tty_driver实例,并编写对应的tty_operations的成员函数。
虽然UART驱动完全可以遵循tty_driver的架构进行设计,但是linux还是在
drivers/tty/serial/serial_core.c中,专门实现了UART的通用TTY层,即串口核心层。这样,TTYDRIVER使用UARTCORE提供的服务,而UARTCORE则向下层提供定义好的驱动接口,由具体的串口驱动来提供服务。
串口核心层定义了uart_driver结构体,及其操作集 uart_ops。
串口核心层会调用uart_ops中的函数接口,向上层提供服务。
struct uart_driver{
struct tty_driver * tty_driver;
const char* driver_name;
const char* dev_name;
int major;
int minor;
struct console* cons;
};
struct uart_ops{
void (*start_tx)(struct uart_port*);
void (*flush_buffer)(*struct uart_port*);
void (*config_port)(*struct uart_port*, int );
...
};
内核中,用uart_port来表示一个UART设备,uart_ops被关联到UART_PORT上,作为UART的操作集。
当uart_driver中的函数接口被调用时,会传入一个uart_port的对象,那么就可以通过uart_port.ops来找到所需要的操作集了。
内核提供了API
uart_register_driver()
用来注册一个UART的驱动。
这样,一个具体的UART设备的驱动编写,就变成填充uart_driver,注册uart_driver,并编写uart_ops了。