关于setup_early_printk()函数,主要用来注册用于启动阶段显示的控制台。内核中声明了一个全局变量early_console,并将定义的全局变量early_console_prom赋值给它,随后开始注册early_console_prom所抽象的终端。上述原型如下:
struct console *early_console;
static void early_console_write(struct console *con, const char *s, unsigned n)
{
while (n-- && *s) {
if (*s == '\n')
prom_putchar('\r');
prom_putchar(*s);
s++;
}
}
static struct console early_console_prom = {
.name = "early",
.write = early_console_write,
.flags = CON_PRINTBUFFER | CON_BOOT,
.index = -1,
};
void __init setup_early_printk(void)
{
if (early_console)
return;
early_console = &early_console_prom;
register_console(&early_console_prom);
}
关于控制台注册函数的原型如下:
void register_console(struct console *newcon)
{
int i;
unsigned long flags;
struct console *bcon = NULL;
struct console_cmdline *c;
static bool has_preferred;
if (console_drivers)
//判断是否具有控制台驱动,console_drivers为全局变量,其类型为struct console。
//如果具有控制台驱动,则检测该控制台驱动是否已注册。
//假设当前位于内核启动的最初阶段,因此该变量为空。
for_each_console(bcon)
if (WARN(bcon == newcon, "console '%s%d' already registered\n", bcon->name, bcon->index))
return;
if (console_driver && newcon->flags & CON_BOOT) {
//如果具有控制台驱动,且所要注册的控制台具有CON_BOOT标识,则检测已有的控制台驱动是否具有CON_BOOT标识,如果没有,则提示所要注册的控制台太迟。
for_each_console(bcon) {
if (!(bcon->flags & CON_BOOT)) {
pr_info("Too late to register bootconsole %s%d\n", newcon->name, newcon->index);
return;
}
}
}
if (console_drivers && console_drivers->flags & CON_BOOT)
//如果具有控制台驱动,且控制台驱动标识为CON_BOOT,则将已存在的控制台赋值给所要注册的控制台。
bcon = console_drivers;
if (!has_preferred || bcon || !console_drivers)
has_preferred = preferred_console >= 0;
if (!has_preferred) {
if (newcon->index < 0)
newcon->index = 0;
//更改所要注册的中断的索引号。即将-1更正为0。
if (newcon->setup == NULL || newcon->setup(newcon, NULL) == 0) {
//如果所要注册的控制台不存在setup函数,或是setup函数返回为0,则修改其flags标识属性。
newcon->flags |= CON_ENABLED;
if (newcon->device) {
//如果该控制台也具有device函数,则执行下面两步操作。
newcon->flags |= CON_CONSDEV;
has_preferred = true;
}
}
}
//在内核启动阶段,console_cmdline数组为空,因此不进入下述的循环结构体中
for (i = 0, c = console_cmdline; i < MAX_CMDLINECONSOLES && c->name[0]; i++, c++) {
//遍历全局数组console_cmdline数组,该数组类型为struct console_cmdline。
if (!newcon->match || newcon->match(newcon, c->name, c->index, c->options) != 0) {
//如果所要注册的控制台不存在match函数,或是执行返回值不为0。则执行以下代码。
BUILD_BUG_ON(sizeof(c->name) != sizeof(newcon->name));
//console_cmdline与console结构体中的name变量的长度均为16。
if (strcmp(c->name, newcon->name) != 0)
continue;
if (newcon->index >= 0 && newcon->index != c->index)
continue;
if (newcon->index < 0)
newcon->index = c->index;
if (_braille_register_console(newcon, c))
return;
if (newcon->setup && newcon->setup(newcon, c->options) != 0)
break;
}
newcon->flags |= CON_ENABLED;
if (i == preferred_console) {
newcon->flags |= CON_CONSDEV;
has_preferred = true;
}
break;
}
if (!(newcon->flags & CON_ENABLED))
//检查所要注册的控制台是否已使能
return;
if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
newcon->flags &= ~CON_PRINTBUFFER;
console_lock();
if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
//如果console_drivers链表为空,且所要注册的设备具有CON_CONSDEV标识,则执行以下操作。
newcon->next = console_drivers;
console_drivers = newcon;
//将所要注册的控制台插入console_drivers链表中
if (newcon->next)
newcon->next->flags &= ~CON_CONSDEV;
} else {
newcon->next = console_drivers->next;
console_drivers->next = newcon;
}
...
if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) && !keep_bootcon) {
for_each_console(bcon)
if (bcon->flags & CON_BOOT)
unregister_console(bcon);
//卸载已注册的控制台驱动
}
}
综上,便是内核中注册控制台驱动的过程。