18_console驱动注册过程#

console驱动注册过程#

  • 参考代码

    • Linux 4.9.88

      kernel/printk.c
      include/linux/kernel.h
      kernel/printk/internal.h
      drivers/tty/serial/imx.c
      
    • Linux 5.4

      kernel/printk.c
      include/linux/kernel.h
      kernel/printk/printk_safe.c
      drivers/tty/serial/stm32-usart.
      

1. 回顾printk的使用#

image-20210813175952791

2. console结构体分析#

include\linux\console.h:

struct console {
	char	name[16];  // name为"ttyXXX",在cmdline中用"console=ttyXXX0"来匹配
    
    // 输出函数
	void	(*write)(struct console *, const char *, unsigned);
    
	int	    (*read)(struct console *, char *, unsigned);
    
    // APP访问/dev/console时通过这个函数来确定是哪个(index)设备
    // 举例:
    // a. cmdline中"console=ttymxc1"
    // b. 则注册对应的console驱动时:console->index = 1
    // c. APP访问/dev/console时调用"console->device"来返回这个index
	struct  tty_driver *(*device)(struct console *co, int *index);
    
	void	(*unblank)(void);
    
    // 设置函数, 可设为NULL
	int	    (*setup)(struct console *, char *);
    
    // 匹配函数, 可设为NULL
	int	    (*match)(struct console *, char *name, int idx, char *options); 
    
	short	flags;
    
    // 哪个设备用作console: 
    // a. 可以设置为-1, 表示由cmdline确定
    // b. 也可以直接指定
	short	index;
    
    // 常用: CON_PRINTBUFFER
	int	    cflag;
	void	*data;
	struct	 console *next;
};

3. console驱动注册过程#

3.1 处理命令行参数#

kernel\printk\printk.c中,可以看到如下代码:

__setup("console=", console_setup);

这是用来处理u-boot通过设备树传给内核的cmdline参数,比如cmdline中有如下代码:

console=ttymxc0,115200  console=ttyVIRT0

对于这两个”console=xxx”就会调用console_setup函数两次,构造得到2个数组项:

struct console_cmdline
{
	char	name[16];			/* Name of the driver	    */
	int	index;				/* Minor dev. to use	    */
	char	*options;			/* Options for the driver   */
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
	char	*brl_options;			/* Options for braille driver */
#endif
};

static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];

在cmdline中,最后的”console=xxx”就是”selected_console”(被选中的console,对应/dev/console):

image-20210816075055595

3.2 register_console#

console分为两类,它们通过console结构体的flags来分辨(flags中含有CON_BOOT):

  • bootconsoles:用来打印很早的信息

  • real consoles:真正的console

可以注册很多的bootconsoles,但是一旦注册real consoles时,所有的bootconsoles都会被注销,并且以后再注册bootconsoles都不会成功。

被注册的console会放在console_drivers链表中,谁放在链表头部?

  • 如果只有一个real consoles,它自然是放在链表头部

  • 如果有多个real consoles,”selected_console”(被选中的console)被放在链表头部

放在链表头有什么好处?APP打开”/dev/console”时,就对应它。

uart_add_one_port
    uart_configure_port
    	register_console(port->cons);

3.3 /dev/console#

drivers\tty\tty_io.c中,代码调用过程如下:

tty_open
    tty = tty_open_by_driver(device, inode, filp);
		driver = tty_lookup_driver(device, filp, &index);
			case MKDEV(TTYAUX_MAJOR, 1): {
                struct tty_driver *console_driver = console_device(index);


/* 从console_drivers链表头开始寻找
 * 如果console->device成功,就返回它对应的tty_driver
 * 这就是/dev/console对应的tty_driver
 */ 
struct tty_driver *console_device(int *index)
{
	struct console *c;
	struct tty_driver *driver = NULL;

	console_lock();
	for_each_console(c) {
		if (!c->device)
			continue;
		driver = c->device(c, index);
		if (driver)
			break;
	}
	console_unlock();
	return driver;
}