u-boot串口和stdio、console初始化及相關操作詳解<三>
來源:程序員人生 發布時間:2016-10-31 11:06:21 閱讀次數:3308次
console是構建在stdio之上的,console的初始化是board_r中最后掃尾的操作。
console的初始化函數console_init_r在common/console.c中實現:
int console_init_r(void)
{
char *stdinname, *stdoutname, *stderrname;
struct stdio_dev *inputdev = NULL, *outputdev = NULL, *errdev = NULL;
#ifdef CONFIG_CONSOLE_MUX
int iomux_err = 0;
#endif
/* set default handlers at first */
gd->jt->getc = serial_getc;
gd->jt->tstc = serial_tstc;
gd->jt->putc = serial_putc;
gd->jt->puts = serial_puts;
gd->jt->printf = serial_printf;
/*--------------------------以上為代碼段1--------------------------------------------*/
/* stdin stdout and stderr are in environment */
/* scan for it */
stdinname = getenv("stdin");
stdoutname = getenv("stdout");
stderrname = getenv("stderr"); //setenv stdout serial,vga標準輸出被重載,如果u-boot中環境變量stdou被設定,那末stdout就被重定位
if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ 這里OVERWRITE_CONSOLE值為1
inputdev = search_device(DEV_FLAGS_INPUT, stdinname);
outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname);
errdev = search_device(DEV_FLAGS_OUTPUT, stderrname);
#ifdef CONFIG_CONSOLE_MUX //如setenv stdout serial,vga
iomux_err = iomux_doenv(stdin, stdinname);
iomux_err += iomux_doenv(stdout, stdoutname);
iomux_err += iomux_doenv(stderr, stderrname);
if (!iomux_err)
/* Successful, so skip all the code below. */
goto done;
#endif
}
/*--------------------------以上為代碼段2--------------------------------------------*/
/* if the devices are overwritten or not found, use default device */
if (inputdev == NULL) {
inputdev = search_device(DEV_FLAGS_INPUT, "serial");
}
if (outputdev == NULL) {
outputdev = search_device(DEV_FLAGS_OUTPUT, "serial");
}
if (errdev == NULL) {
errdev = search_device(DEV_FLAGS_OUTPUT, "serial");
}
/*--------------------------以上為代碼段3--------------------------------------------*/
/* Initializes output console first */
if (outputdev != NULL) {
/* need to set a console if not done above. */
console_doenv(stdout, outputdev);
}
if (errdev != NULL) {
/* need to set a console if not done above. */
console_doenv(stderr, errdev);
}
if (inputdev != NULL) {
/* need to set a console if not done above. */
console_doenv(stdin, inputdev);
}
/*--------------------------以上為代碼段4--------------------------------------------*/
#ifdef CONFIG_CONSOLE_MUX
done:
#endif
#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET /*defined*/
stdio_print_current_devices();
#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */
#ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE /*no defined*/
/* set the environment variables (will overwrite previous env settings) */
for (i = 0; i < 3; i++) { setenv(stdio_names[i], stdio_devices[i]->name);
}
#endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */ /*defined*/
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL);
/*--------------------------以上為代碼段5--------------------------------------------*/
return 0;
}
上述程序按其實現的功能可分為5部份,為了便于分析,我們下面僅以stdout裝備為例,逐漸進行討論:
1. gd->jt初始化------代碼段1
gd->jt->getc = serial_getc;
gd->jt->tstc = serial_tstc;
....
上述的代碼段設置jt操作的默許函數為串口相干函數。關于gd->jt所包括函數的使用,我們將在后續的章節中討論。
2.stdin, stdout, stderr裝備被環境變量中的設定值重定位---代碼段2
取決于OVERWRITE_CONSOLE配置,當其被配置為0時,標準輸入輸出裝備將使用環境變量stdin,stdout,stderr中的設定值。
代碼段2包括的代碼段以下:
stdinname =getenv("stdin");
stdoutname = getenv("stdout");
stderrname = getenv("stderr"); /*setenv stdout serial,vga標準輸出被重載,如果u-boot中環境變量stdou被設定,那末stdout就被重定位*/
if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ /* OVERWRITE_CONSOLE或為宏定義,或為函數返回值,這里為返回值1*/
inputdev = search_device(DEV_FLAGS_INPUT, stdinname);
outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname);
errdev = search_device(DEV_FLAGS_OUTPUT, stderrname);
#ifdef CONFIG_CONSOLE_MUX //如setenv stdout serial,vga
iomux_err = iomux_doenv(stdin, stdinname);
iomux_err += iomux_doenv(stdout, stdoutname);
iomux_err += iomux_doenv(stderr, stderrname);
if (!iomux_err)
/* Successful, so skip all the code below. */
goto done;
#endif
如我們曾在u-boot中履行命令:
=>setenv stdout vga
或
=>setenv stdout serial, vga
(注意:只有在定義了CONFIG_CONSOLE_MUX了時,才能將多個裝備賦值給stdio相干的環境變量,否則履行上述命令u-boot會返回毛病信息。)
然后履行saevenv命令保存環境變量,重啟u-boot,代碼履行到這里,如果OVERWRITE_CONSOLE的值為0,代碼繼續向下履行,
針對環境變量的設定值,有兩種情況,我們以stdout裝備為例,分析其處理流程。
如環境變量stdout的值為"vga",stdout設定為單1的stdio裝備vga,search_device(DEV_FLAGS_OUTPUT, stdoutname)的返回值
outputdev為非空,且指向裝備名為"vga"的裝備。
如環境變量stdout的值為"serial,vga ",則search_device(DEV_FLAGS_OUTPUT, stdoutname)找不到裝備名為"serial,vga"的裝備,
返回值outputdev為NULL。
接下來的宏CONFIG_CONSOLE_MUX處理上述某1stdio包括多路裝備的情況,如輸出信息同時輸出到多個裝備的情況。
上述代碼中search_device語句放在iomux_doenv以后應當更公道。即:
stdoutname = getenv("stdout"); /*setenv stdout serial,vga標準輸出被重載,如果u-boot中環境變量stdou被設定,那末stdout就被重定位*/
...
if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */ /* OVERWRITE_CONSOLE或為宏定義,或為函數返回值,這里為返回值1*/
#ifdef CONFIG_CONSOLE_MUX //如setenv stdout serial,vga
iomux_err = iomux_doenv(stdin, stdinname);
...
if (!iomux_err)
/* Successful, so skip all the code below. */
goto done;
#endif
inputdev = search_device(DEV_FLAGS_INPUT, stdinname);
...
這樣,當定義了CONFIG_CONSOLE_MUX時,如果iomux_doenv的履行沒有毛病,那末跳過search_device。如果有毛病,則進1步履行search_device段代碼。
如果沒有定義CONFIG_CONSOLE_MUX,則直接履行search_device。原來的程序流程安排中,當定義了CONFIG_CONSOLE_MUX時,會先履行那些search_device代碼,后續履行iomux_doenv后,大多數情況下iomux_doenv的履行不會有毛病,這樣直接goto done,search_device的返回值根本用不到,這時候先履行的search_device有些過剩。對照原程序流程安排,更改后的代碼邏輯性比較強,履行效力提高,但可讀性有點差。
這里還要強調的是:
標志OVERWRITE_CONSOLE(或是宏,或是函數的返回值)決定著標準輸入輸出裝備是不是使用非易失性存儲器存儲的環境變量stdin,stdout,stderr中的設定值,即是不是被后者重載。如果OVERWRITE_CONSOLE 的值為0,那末u-boot啟動后,stdio裝備將使用重載值。否則,會使用默許的serial裝備(見代碼段3)。
當在u-boot中履行命令setenv stdout ...時,其類似于linux中履行了的輸入輸出重定位命令">"。該命令也是立即生效的。但setenv stdout ...并未調用代碼段2。而是調用了某些回調函數,進行了stdio重定位。所以,u-boot命令setenv stdout ...強調的是stdio的重定位到指定的裝備。
要注意辨別stdio被環境變量重載和stdio重定位的區分。
下面我們重點分析iomux_doenv函數。
iomux_doenv函數包括的代碼比較多,下面刪除注解和1些返回值的判斷處理,且只保存了stdout分支,且按實現功能將其分為3段,其大致的處理框架以下:
#ifdef CONFIG_CONSOLE_MUX
/* This tries to preserve the old list if an error occurs. */
int iomux_doenv(const int console, const char *arg)
{
char *console_args, *temp, **start;
int i, j, k, io_flag, cs_idx, repeat;
struct stdio_dev *dev;
struct stdio_dev **cons_set;
console_args = strdup(arg);
...
i = 0;
temp = console_args;
for (;;) {
temp = strchr(temp, ',');
if (temp != NULL) {
i++;
temp++;
continue;
}
/* There's always one entry more than the number of commas. */
i++;
break;
}
start = (char **)malloc(i * sizeof(char *));
...
/* setenv stdout serial,vga 幾個用逗號分隔的參數*/
i = 0;
start[0] = console_args;
for (;;) {
temp = strchr(start[i++], ',');
if (temp == NULL)
break;
*temp = '\0';
start[i] = temp + 1;
}
/*start是1個指向字符串的指針數組。這里start[0]指向serial, start[1]指向vga*/
/*--------------------------以上為代碼段2.1 --------------------------------------------*/
cons_set = (struct stdio_dev **)calloc(i, sizeof(struct stdio_dev *));
/*...cons_set檢查,出錯返回1*/
switch (console) {
case stdout:
io_flag = DEV_FLAGS_OUTPUT;
break;
default:
/*...釋放資源start,console_args,cons_set*/
return 1;
}
cs_idx = 0;
for (j = 0; j < i; j++) { dev = search_device(io_flag, start[j]); if (dev == NULL) continue; repeat = 0; for (k = 0; k < cs_idx; k++) { if (dev == cons_set[k]) { repeat++; break; } } if (repeat) continue; if (console_assign(console, start[j]) < 0) continue; cons_set[cs_idx++] = dev; } /*--------------------------以上為代碼段2.2 --------------------------------------------*/ free(console_args); free(start); /* failed to set any console */ if (cs_idx == 0) { free(cons_set); return 1; } else { console_devices[console] = (struct stdio_dev **)realloc(console_devices[console], cs_idx * sizeof(struct stdio_dev *)); if (console_devices[console] == NULL) { free(cons_set); return 1; } memcpy(console_devices[console], cons_set, cs_idx * sizeof(struct stdio_dev *)); cd_count[console] = cs_idx; } free(cons_set); /*--------------------------以上為代碼段2.3 --------------------------------------------*/ return 0; } #endif /* CONFIG_CONSOLE_MUX */
上述程序中的stdin,stdout,stderr在include/common.h中定義:
#define stdin 0
#define stdout 1
#define stderr 2
#define MAX_FILES 3
討論上述代碼之前,首先要強調的是,只有定義了CONFIG_CONSOLE_MUX,才會有函數iomux_doenv的定義和實現。
否則,不會使用此函數。
代碼段2.1
這里主要處理stdout包括多個裝備的情況,而單個裝備可看做多個裝備的特例。多個裝備的裝備名使用逗號分開,就像我們在上面的代碼段2中討論的1樣,當stdio被環境變量的設定值重載時,可能包括的多個裝備的裝備名用逗號分開,如環境變量stdout的值為“ serial,vga”,該段代碼終究將這些裝備名字符串的首址存入start[i]字符串指針數組中,i為用逗號隔開的裝備名個數。
代碼段2.2
首先利用上述start[i]字符串指針數組指向的裝備名查找在全局裝備表devs中查找此前已注冊的stdio裝備,并去掉裝備重復(如設定stdout環境變量為serial,vga,serial),然后調用console_assign:
int console_assign(int file, const char *devname)
{
int flag;
struct stdio_dev *dev;
/* Check for valid file */
switch (file) {
case stdin:
flag = DEV_FLAGS_INPUT;
break;
case stdout:
case stderr:
flag = DEV_FLAGS_OUTPUT;
break;
default:
return ⑴;
}
/* Check for valid device name */
dev = search_device(flag, devname);
if (dev)
return console_setfile(file, dev);
return ⑴;
}
為了此處的討論盡量清晰簡單,search_device函數我們放在后面分析。
在履行console_assign之前,已調用過search_device獲得了裝備指針,而后調用的console_assign中又履行了1遍search_device,然后調用了onsole_setfile,查找裝備的操作有些冗余,為什么不在iomux_doenv中直接調用onsole_setfile,來代替console_assign呢?
對外部利用程序來講,只關心和知道裝備名,所以調用console_assign,利用裝備裝備名查找到對應的裝備,然后再調用onsole_setfile。這是比較公道的。console_assign是文件console.c中開放給外部程序的唯1stdio分配操作的函數接口。onsole_setfile則是console.c中的靜態函數。所以針對外部文件中的函數iomux_doenv相干的stdio分配操作,調用了console_assign ,即便有些代碼冗余,就其程序架構上公道性,該冗余還是能容忍的。
下面分析函數console_setfile:
static int console_setfile(int file, struct stdio_dev * dev)
{
int error = 0;
if (dev == NULL)
return ⑴;
switch (file) {
case stdin:
case stdout:
case stderr:
/* Start new device */
if (dev->start) {
error = dev->start(dev);
/* If it's not started dont use it */
if (error < 0) break; } /* Assign the new device (leaving the existing one started) */ stdio_devices[file] = dev; /* * Update monitor functions * (to use the console stuff by other applications) */ switch (file) { case stdin: gd->jt->getc = getc;
...
break;
case stdout:
...
gd->jt->printf = printf;
break;
}
break;
default: /* Invalid file ID */
error = ⑴;
}
return error;
}
首先嘗試啟動入口參數中的stdio裝備。需要注意的是,在前面"stdio_add_devices"1節stdio裝備的注冊中,只是填充了相干的結構體,如果其后沒有被使用(如serial就曾被使用了),就還未真正實際啟動被注冊的裝備。而這里,為console分配stdio裝備時,就要啟動它(實際是初始化該硬件裝備),由于接下就要使用該硬件完成stdio實際的硬件輸入輸出操作。上述dev->start代碼中,1旦啟動失敗(有可能已啟動,或硬件本身的緣由),函數console_setfile就立即返回,返回值為0。console_setfile的上層函數也返回0,這樣就回到代碼段2.2,但接下來還是會填充cons_set,但不會在函數console_setfile中接著填充下面的stdio_devices。
另外還有1種情況,如video裝備,在video注冊為stdio裝備時,并未填充start函數,那末,start為NULL,即該裝備無需有啟動操作便可使用,那末這里,該裝備也會填充到stdio_devices中。
接下來函數console_setfile將上述查找到的裝備終究存儲在全局變量stdio_devices中。如上所述,此裝備是被成功啟動或可用的裝備,
stdio_devices在common/stdio.c中定義為:
struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL };
它是1個裝備指針數組,該數組有3個成員,即stdin,stdout,stderr,代表當前正在使用的stdio裝備。所以,如果是stdout裝備,該裝備的結構體首址存入到stdio_devices[1]中。其他類此。當被重載后stdio為多個裝備,如stdout環境變量設定為serial,vga,屢次調用的console_assign也會屢次履行console_setfile,如果兩裝備都有效,且能被成功啟動,那末就會對同1個stdio_devices[stdout]進行賦值,可以看到stdio_devices[1]的值終究為裝備名為"vga"的裝備,第1個serial裝備會被覆蓋掉。即這時候只使用環境變量設定值的最后1個可被啟動的有效裝備。
另外在后續的信息輸出的stdout使用時,我們可以看到printf調用了fputs,fputs又調用了console_puts,這時候,根據CONFIG_CONSOLE_MUX的定義,console_puts有兩處實現,當定義了CONFIG_CONSOLE_MUX,即stdout裝備可多路輸出, console_puts使用console_devices數組中的裝備進行輸入輸出的相干操作;
當沒有定義了CONFIG_CONSOLE_MUX時,直接調用stdio_devices[file]->puts(stdio_devices[file], s)。但明顯,當定義了CONFIG_CONSOLE_MUX時,后續的輸入輸出操作中幾近不會使用到stdio_devices,此中情況下也被填充,其意義不是很大。
其實stdio_devices變量主要用在CONFIG_CONSOLE_MUX未定義的情況下。此時,最后的輸入輸出操作會使用該變量中存儲的裝備來完成。
README.iomux中有:
It should be possible to specify any device which console_assign()
finds acceptable, but the code has only been tested with serial and
nc.
程序最后將更新gd->jt函數列表。
代碼段2.3
該段代碼主要實現:
將上面代碼段查找到的裝備存儲到全局變量console_devices[console]中,其裝備個數存儲到全局變量cd_count[console]中。
這里的console即stdin,stdout,stderr常量之1。當這3者之1具有多個stdio裝備時,console_devices[console]會保存這多個裝備,且用cd_count[console]來記錄裝備個數。如環境變量stdout的值為serial,vga,那末console_devices[1]指向的struct stdio_dev結構體指針數組中會包括兩個指針,分別指向serial和vga裝備對應的結構體地址。
cd_count[1]為console_devices[1]指向的數組的長度,這里值為2。
我們可以在終究的輸出函數console_puts實現中看到console_devices和cd_count的使用:
static void console_puts(int file, const char *s)
{
int i;
struct stdio_dev *dev;
for (i = 0; i < cd_count[file]; i++) { dev = console_devices[file][i]; if (dev->puts != NULL)
dev->puts(dev, s);
}
}
函數iomux_doenv總結:
該函數填充了以下的全局變量:
stdio_devices[3]
console_devices[3]
cd_count[3]
stdio_devices[3]在common/stdio.c中定義。還未被使用過。
console_devices、cd_count在common/console.c中定義為:
static struct stdio_dev *tstcdev;
struct stdio_dev **console_devices[MAX_FILES];
int cd_count[MAX_FILES];
其中的MAX_FILES在 include/common.h中定義為3,即stdin,stdout,stderr。
console_devices包括控制臺所用的struct stdio_dev裝備,控制臺裝備包括標準輸入,輸出和毛病裝備。而每項標準裝備會有包括多個struct stdio_dev裝備的情況。如控制臺同時輸出到串口和液晶。cd_count[...]的值這類所多個包括struct stdio_dev裝備的計數。如console_devices[1]是指向stdout裝備數組的指針,而cd_count[1]是stdout裝備所包括的個數。這里的1即stdout。其他類此。
stdio_devices[...]則包括了當前的標準輸入輸出和出錯裝備。如 stdio_devices[0]為當前標準輸入裝備,
stdio_devices[1]為當前標準輸出, stdio_devices[2]為當前標準出錯。
當某項標準stdio裝備包括多個裝備時,只是使用了環境變量相對應設定中的最后1項,
比如我們設定了:
setenv stdout serial,vga
那末stdio_devices[1]的值是裝備名為"vga"的裝備。
3. 查找裝備---代碼段3
前面的處理包括了以下的情況:
a). OVERWRITE_CONSOLE != 0,即stdio裝備沒有被環境變量重載。無路是不是定義了CONFIG_CONSOLE_MUX ,
這將直接履行到代碼段3
b). OVERWRITE_CONSOLE == 0, 即stdio裝備被環境變量重載,且定義了CONFIG_CONSOLE_MUX,
stdio裝備環境變量中包括的裝備無效或stdio裝備console注冊失敗,也將履行代碼段3。
否則如果注冊成功,跳過代碼段3⑷。
所以,只要程序履行到了代碼段3,都將使用串口(serial)作為默許的stdio裝備。
下面我們具體分析該代碼段所包括函數的具體實現。以stdout裝備為例,簡化后代碼段3以下:
if (outputdev == NULL) {
outputdev = search_device(DEV_FLAGS_OUTPUT, "serial");
}
search_device函數履行裝備查找,其輸入參數DEV_FLAGS_OUTPUT為stdin,stdout,stderr對應的3者之1。
另外一個參數為裝備名,即根據裝備名來查找裝備。
search_device函數的在common/console.c中實現以下:
struct stdio_dev *search_device(int flags, const char *name)
{
struct stdio_dev *dev;
dev = stdio_get_by_name(name);
if (dev && (dev->flags & flags))
return dev;
return ((void *)0);
}
函數stdio_get_by_name在common/stdio.c中實現:
struct stdio_dev* stdio_get_by_name(const char *name)
{
struct list_head *pos;
struct stdio_dev *dev;
if(!name)
return NULL;
list_for_each(pos, &(devs.list)) {
dev = list_entry(pos, struct stdio_dev, list);
if(strcmp(dev->name, name) == 0)
return dev;
}
return NULL;
}
在前面"stdio_add_devices"函數討論的1節中,所有注冊的stdio裝備使用全局變量devs.list鏈表串接起來,stdio_get_by_name函數就是在此鏈表中查找名字為涵參name的stdio裝備。我們繼續跟蹤list_entry,可以看到其定義為:
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
container_of和linux驅動中用法1致,也是通過結構體成員來查找結構體本身的首址。我們在前面的"stdio_add_devices"
1節中也曾提及過,devs.list的鏈表成員只是struct stdio_dev結構體的成員變量list,而非struct stdio_dev結構體變量本身。所以要使用container_of查找描寫stdio裝備的struct stdio_dev變量本身。stdio_get_by_name履行完返回到search_device后,如果找到裝備,還會核對查找到的裝備屬性標志是不是和輸入參數的標志1致,裝備屬性標志在該裝備注冊時設置。
4. 注冊stdio裝備到console中---代碼段4
我們要注意代碼履行到代碼段4的前述情況:
使用串口serial作為唯1的默許stdio裝備進行console注冊。
針對CONFIG_CONSOLE_MUX定義與否,函數console_doenv有兩處實現。當沒有定義CONFIG_CONSOLE_MUX時,則console_doenv的實現為:
static inline void console_doenv(int file, struct stdio_dev *dev)
{
console_setfile(file, dev);
}
函數console_setfile將上述查找到的裝備終究存儲在全局變量stdio_devices中。這我們在上面代碼段2.2已討論過。終究將默許的serial裝備賦值到stdio_devices中去。
綜上所述,當沒有定義CONFIG_CONSOLE_MUX,裝備是不會注冊到console的裝備描寫變量console_devices中去的。當定義了CONFIG_CONSOLE_MUX時,函數console_doenv則直接調用common/Iomux.c中的iomux_doenv。后續操作和上述代碼段2.x中的操作1致,只不過這里的裝備為單1裝備serial,會將serial注冊到console中去。不知是不是是1種代碼過渡,截止到u-boot⑵016.3,個人覺得還是將stdio和console混淆處理的模糊不清。
5.函數的尾端程序處理---代碼段5
后續的程序包括:
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL);
由于CONFIG_PRE_CONSOLE_BUFFER沒有定義,print_pre_console_buffer為空函數。
gd->flags是1個非常重要的定義,當履行gd->flags |= GD_FLG_DEVINIT后,代表此時console控制臺已的前戲準備工作已完成,console控制臺已可用。
gd->flags會被console.c文件中的函數屢次判標GD_FLG_DEVINIT使用。 這些函數包括getc,tstc,puts,on_console。
如最經常使用的信息輸出函數printf調用了這其中的puts。下面以puts函數為例,分析變量gd->flags使用及意義:
void puts(const char *s)
{
...
if(!gd->have_console)
return pre_console_puts(s);
if (gd->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fputs(stdout, s);
} else {
/* Send directly to the handler */
pre_console_puts(s);
serial_puts(s);
}
}
上述代碼中,如果gd->flags & GD_FLG_DEVINIT為真時,將使用fputs履行信息輸出,fputs定義為:
void fputs(int file,constchar*s)
{
if (file < MAX_FILES) console_puts(file, s); }
函數console_puts我們在代碼段2.2中粗略提及過,其具體實現以下:
static void console_puts(int file, const char *s)
{
int i;
struct stdio_dev *dev;
for (i = 0; i < cd_count[file]; i++) { dev = console_devices[file][i]; if (dev->puts != NULL)
dev->puts(dev, s);
}
}
可見,gd->flags & GD_FLG_DEVINIT為真時,終究將使用console_devices中注冊過的控制臺函數履行相干操作。也即是,履行了代表gd->flags |= GD_FLG_DEVINIT后,gd->flags & GD_FLG_DEVINIT為真 ,代表console控制臺中的相干操作函數可用了。否則使用默許的串口輸出函數serial_puts。
斟酌到這樣1種情況,我們在上述代碼段5的語句
gd->flags |= GD_FLG_DEVINIT;
之前的printf輸出信息,將會使用默許的串口輸出函數serial_puts,該函數在board_f階段被注冊且其后續可用。而該代碼段以后的程序,所使用的printf,都將使用該節討論的console控制臺輸出函數。
gd->flags |= GD_FLG_DEVINIT語句制造了1個這樣的分水嶺。
那末在stdio和serial結構圖的基礎上,加上console,3者之間的結構總圖以下:
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈