uboot 포트와 표준 입력 출력 코드 설명
14755 단어 bootloader
1,displaybanner 및 printcpuinfo
uboot 시작부터 첫 번째 인쇄는 디스플레이 호출banner, U-Boot 2012.10-xxx 정보를 인쇄합니다.이어서 print 호출cpuinfo에서 cpu 정보를 출력합니다.
const char version_string[] = U_BOOT_VERSION" (" U_BOOT_DATE " - " U_BOOT_TIME ")"CONFIG_IDENT_STRING;
static int display_banner (void)
{
printf ("
%s
", version_string);
debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lX
",
_armboot_start, _bss_start, _bss_end);
……
return (0);
}
2. Uboot은 어떻게 직렬 포트를 초기화합니까?
Uboot 2단계 입구 함수 boardinit_f init 통과sequence 초기화 배열에서 5개의 직렬 관련 함수를 호출했습니다.
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
이 몇 개의 함수는 각각 다음과 같은 작업을 했다.우선 전송 속도 설정을 가져옵니다.여기에서 얻은 포트레이트 값을 gd->baudrate에 저장합니다. 포트레이트가 초기화될 때 사용됩니다.
static int init_baudrate(void)
{
int ret;
ret = early_access_env_vars();
/* If the above call succeeded, gd's baudrate gets initialized within the
* call
*/
if (ret == -1) {
gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);
}
return 0;
}
2. 직렬 초기화, serialinit 함수는drivers/serial 디렉터리에 정의되어 있으며, 이 디렉터리에는 다양한 직렬 드라이브가 있습니다.makefile는 이전 uboot의 설정을 통해 지정한 직렬 드라이브를 컴파일합니다.직렬 드라이브에 serial 포함init, serial_getc, serial_putc, serial_puts, serial_tstc, serial_setbrg의 기본 API 함수입니다.직렬 인쇄와 표준 입력 출력의 기능은 모두 이러한 하부 함수에 의존하여 실현된다.이 단계가 완성된 후에 우리의 직렬은 사용할 수 있다.putc,puts,getc를 사용할 수 있습니다.여기서 우리는 구체적인 CPU 플랫폼의 직렬 구동 코드를 분석하지 않는다.자세한 내용은 특정 프로세서의 직렬 데이터 매뉴얼을 참조하십시오.3.console_init_f 이 함수의 역할은 포트가 초기화되기 전의puts나putc의 인쇄 내용을 출력하는 것이다.다시 말하면 포트가 초기화되기 전에도putc,puts 함수를 호출할 수 있다.직렬 초기화가 성공할 때까지 기다려야 출력이 출력됩니다.
console_init_f()--> print_pre_console_buffer()
static void print_pre_console_buffer(void)
{
unsigned long i = 0;
char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;
if (gd->precon_buf_idx > CONFIG_PRE_CON_BUF_SZ)
i = gd->precon_buf_idx - CONFIG_PRE_CON_BUF_SZ;
while (i < gd->precon_buf_idx)
putc(buffer[CIRC_BUF_IDX(i++)]);
}
여기 메모리 주소에 있는 CONFIG 를 봤어요.PRE_CON_BUF_ADDR의 길이는 CONFIG 입니다.PRE_CON_BUF_SZ의 메모리 영역입니다.포트가 작동하지 않기 전에putc의 내용을 이 그룹에 임시로 저장해서 포트가 출력을 출력할 수 있도록 합니다.putc 소스 구현을 살펴보겠습니다.
void putc(const char c)
{
#ifdef CONFIG_SILENT_CONSOLE
if (gd->flags & GD_FLG_SILENT)
return;
#endif
#ifdef CONFIG_DISABLE_CONSOLE
if (gd->flags & GD_FLG_DISABLE_CONSOLE)
return;
#endif
if (!gd->have_console)
return pre_console_putc(c);
if (gd->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fputc(stdout, c);
} else {
/* Send directly to the handler */
serial_putc(c);
}
}
만약!gd->have_console, 즉 포트가 초기화되지 않았을 때pre 호출console_putc(c) 함수, 이 함수는 인쇄할 문자를 주소CONFIG 에 직접 저장합니다PRE_CON_BUF_ADDR buf에 들어가세요.만약 직렬 포트가 이미 사용 가능하다면 gd->flags & GD 를 판단해야 한다FLG_DEVINIT는 표준 입력 출력 장치(stdio)가 초기화되었는지 확인하고, stdio가 초기화되면 GD 를 설정합니다.FLG_DEVINIT 로고 위치, 이때 fputc(stdout, c)를 호출합니다.stdio 처리를 보내지 않으면 직렬 밑바닥 API 함수serialputc(c) 출력.기타 함수, 예를 들어Puts,fputc,getc,tstc 등 함수는putc를 참조할 수 있는 실현 방식을 실현한다.4. 이때 직렬 입력 출력은 이미 OK입니다.printf의 실현을 분석해 봅시다.
int printf(const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer[CONFIG_SYS_PBSIZE];
#ifndef CONFIG_PRE_CONSOLE_BUFFER
if (!gd->have_console)
return 0;
#endif
va_start(args, fmt);
/* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
/* Print the string */
puts(printbuffer);
return i;
}
우리는 printf가puts를 호출해서 포트에 출력하는 것을 보았다.직렬 초기화가 완료되기 전에 printf를 호출하면 됩니다.CONFIG를 구성한 경우PRE_CONSOLE_BUFFER는 CONFIG 에 저장됩니다.PRE_CON_BUF_ADDR 메모리에서 구성이 없으면 처리되지 않습니다.
3. 스튜디오 설비 초기화
board_init_r—> serial_initialize, stdio_init, console_init_r 다음boardinit_r은 이 세 함수를 호출해서 스튜디오를 만들 것이다.일단 Serialinitialize는 직렬 장치를 등록합니다.등록은 실제적으로 이전 직렬 초기화 함수를 장치 구조체 바늘에 마운트하는 것입니다.다음 stdio 호출init, Serial 을 호출합니다stdio_stdio 만들기dev 설비 구조체.stdio_register(&dev);장치가 체인 테이블에 등록되어 있습니다.이렇게 하면 직렬 스튜디오 장치 하나가 OK됩니다.호출 프로세스:Stdioinit—-> serial_stdio_init—-> stdio_register.
void serial_stdio_init(void)
{
struct stdio_dev dev;
struct serial_device *s = serial_devices;
while (s) {
memset(&dev, 0, sizeof(dev));
strcpy(dev.name, s->name);
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
dev.start = s->init;
dev.stop = s->uninit;
dev.putc = s->putc;
dev.puts = s->puts;
dev.getc = s->getc;
dev.tstc = s->tstc;
stdio_register(&dev);
s = s->next;
}
}
다음 console 호출init_r 위에서 만든 stdio 장치를 파일 설명자에 대한 표준 입력, 출력, 오류를 연결합니다.
int console_init_r(void)
{
struct stdio_dev *inputdev = NULL, *outputdev = NULL;
int i;
struct list_head *list = stdio_get_list();
struct list_head *pos;
struct stdio_dev *dev;
#ifdef CONFIG_SPLASH_SCREEN
/*
* suppress all output if splash screen is enabled and we have
* a bmp to display. We redirect the output from frame buffer
* console to serial console in this case or suppress it if
* "silent" mode was requested.
*/
if (getenv("splashimage") != NULL) {
if (!(gd->flags & GD_FLG_SILENT))
outputdev = search_device (DEV_FLAGS_OUTPUT, "serial");
}
#endif
/* Scan devices looking for input and output devices */
list_for_each(pos, list) {
dev = list_entry(pos, struct stdio_dev, list);
if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
inputdev = dev;
}
if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
outputdev = dev;
}
if(inputdev && outputdev)
break;
}
/* Initializes output console first */
if (outputdev != NULL) {
console_setfile(stdout, outputdev);
console_setfile(stderr, outputdev);
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdout][0] = outputdev;
console_devices[stderr][0] = outputdev;
#endif
}
/* Initializes input console */
if (inputdev != NULL) {
console_setfile(stdin, inputdev);
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdin][0] = inputdev;
#endif
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
stdio_print_current_devices();
/* Setting environment variables */
for (i = 0; i < 3; i++) {
setenv(stdio_names[i], stdio_devices[i]->name);
}
return 0;
}
Stdio 초기화 후 stdio를 통해print_current_devices 함수는 stdio 장치 이름을 출력합니다.
void stdio_print_current_devices(void)
{
#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET
/* Print information */
puts("In: ");
if (stdio_devices[stdin] == NULL) {
puts("No input devices available!
");
} else {
printf ("%s
", stdio_devices[stdin]->name);
}
puts("Out: ");
if (stdio_devices[stdout] == NULL) {
puts("No output devices available!
");
} else {
printf ("%s
", stdio_devices[stdout]->name);
}
puts("Err: ");
if (stdio_devices[stderr] == NULL) {
puts("No error devices available!
");
} else {
printf ("%s
", stdio_devices[stderr]->name);
}
#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */
}
void fputc(int file, const char c)
{
if (file < MAX_FILES)
console_putc(file, c);
}
static inline void console_putc(int file, const char c)
{
stdio_devices[file]->putc(c);
}
이로써 UBOOT 직렬 포트 및 stdio가 초기화되었습니다.