跳到主要内容

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;
}