uboot 포트와 표준 입력 출력 코드 설명

14755 단어 bootloader
여기에서 uboot 직렬 장치 초기화와 직렬 stdio 표준 입력 출력 장치의 초기화 과정을 분석해 보겠습니다.

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가 초기화되었습니다.

좋은 웹페이지 즐겨찾기