setup_early_printk()函數分析

關於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);
				//卸載已註冊的控制檯驅動
	}
}

綜上,便是內核中註冊控制檯驅動的過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章