關於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);
//卸載已註冊的控制檯驅動
}
}
綜上,便是內核中註冊控制檯驅動的過程。