DSP

uclinux内核的console(2):early console

2019-07-13 15:52发布

rev 0.2   快乐虾 http://blog.csdn.net/lights_joy/ lights@hb165.com   本文适用于 ADI bf561 DSP 优视BF561EVB开发板 uclinux-2008r1.5-rc3 (smp patch) Visual DSP++ 5.0 (update 5)   欢迎转载,但请保留作者信息   在内核启动初期,为了尽可能早地输出一些调试信息,可以在配置时选择使用early console,其选项为CONFIG_EARLY_PRINTK,然后通过earlyprintk=??传递一个参数进去。当内核检测到earlyprintk这一参数时,它将调用setup_early_printk函数初始化BF561内部的UART,然后注册一个console,这样printk的信息就可以通过串口输出。earlyprintk参数的分析参见《uclinux内核参数处理(5)earlyprintk》。

1.1    全局变量

1.1.1   bfin_serial_pops

这个全局变量用以指定对bf561内部串口进行操作的一些回调函数,其定义为: static struct uart_ops bfin_serial_pops = {      .tx_empty = bfin_serial_tx_empty,      .set_mctrl    = bfin_serial_set_mctrl,      .get_mctrl    = bfin_serial_get_mctrl,      .stop_tx = bfin_serial_stop_tx,      .start_tx = bfin_serial_start_tx,      .stop_rx = bfin_serial_stop_rx,      .enable_ms    = bfin_serial_enable_ms,      .break_ctl    = bfin_serial_break_ctl,      .startup = bfin_serial_startup,      .shutdown = bfin_serial_shutdown,      .set_termios  = bfin_serial_set_termios,      .type         = bfin_serial_type,      .release_port = bfin_serial_release_port,      .request_port = bfin_serial_request_port,      .config_port  = bfin_serial_config_port,      .verify_port  = bfin_serial_verify_port, };    

1.1.2   bfin_serial_ports

这个全局变量的定义为: struct bfin_serial_port bfin_serial_ports[NR_PORTS]; 在这里NR_PORTS的值为1 这个全局变量的初始化由bfin_serial_init_ports函数完成,经过此函数的初始化后,此变量的值为: struct bfin_serial_port {         struct uart_port        port; struct uart_port {      spinlock_t         lock;              /* port lock */      unsigned int       iobase;            /* in/out[bwl] */      unsigned char __iomem  *membase;     /*指向UART_THR(0xFFC0 0400),即UART的发送寄存器。 */      unsigned int       irq;          /* IRQ_UART_RX */      unsigned int       uartclk;      /* SCLK */      unsigned int       fifosize;     /* tx fifo size */      unsigned char      x_char;            /* xon/xoff char */      unsigned char      regshift;     /* reg offset shift */      unsigned char      iotype;            /* UPIO_MEM,以直接寄存器访问方式进行操作 */      unsigned char      unused1;        unsigned int       read_status_mask;  /* driver specific */      unsigned int       ignore_status_mask;    /* driver specific */      struct uart_info   *info;             /* pointer to parent info */      struct uart_icount icount;            /* statistics */        struct console         *cons;             /* struct console, if any */        upf_t              flags;     /* UPF_BOOT_AUTOCONF */        unsigned int       mctrl;             /* current modem ctrl settings */      unsigned int       timeout;      /* character-based timeout */      unsigned int       type;              /* port type */      const struct uart_ops  *ops;         /* 指向bfin_serial_pops */      unsigned int       custom_divisor;      unsigned int       line;              /* 串口序号,取0 */      unsigned long      mapbase;      /*指向UART_THR(0xFFC0 0400),即UART的发送寄存器。 */      struct device      *dev;              /* parent device */      unsigned char      hub6;              /* this should be in the 8250 driver */      unsigned char      unused[3];      void          *private_data;         /* generic platform data pointer */ };           unsigned int            old_status;          unsigned int lsr; #ifdef CONFIG_SERIAL_BFIN_DMA      int           tx_done;           /* 初始化为1 */      int           tx_count;          /* 初始化为0 */      struct circ_buf        rx_dma_buf;      struct timer_list       rx_dma_timer;      int           rx_dma_nrows;      unsigned int       tx_dma_channel;  /* 18,即CH_UART_TX */      unsigned int       rx_dma_channel;  /* 17,即CH_UART_RX */      struct work_struct tx_dma_workqueue; #endif };  

1.1.3   bfin_early_serial_console

这个就是得以使用的early console的全局变量,其定义为: static struct __init console bfin_early_serial_console = {      .name = "early_BFuart",      .write = early_serial_write,      .device = uart_console_device,      .flags = CON_PRINTBUFFER,      .setup = bfin_serial_console_setup,      .index = -1,      .data  = &bfin_serial_reg, }; 经过初始化后,flags的值将变为 CON_PRINTBUFFER | CON_BOOT  

1.1.4   bfin_serial_reg

这个全局变量的定义为: static struct uart_driver bfin_serial_reg = {      .owner             = THIS_MODULE,      .driver_name       = "bfin-uart",      .dev_name     = BFIN_SERIAL_NAME,      .major             = BFIN_SERIAL_MAJOR,      .minor             = BFIN_SERIAL_MINOR,      .nr           = NR_PORTS,      .cons              = BFIN_SERIAL_CONSOLE, };      

1.2    初始化过程

1.2.1   setup_early_printk

当内核调用此函数时,仅仅做了一些最基本的硬件初始化工作,如CCLKEBIU等。下面看看它的实现: int __init setup_early_printk(char *buf) {        /* Crashing in here would be really bad, so check both the var         and the pointer before we start using it       */      if (!buf)          return 0;        if (!*buf)          return 0;        if (early_console != NULL)          return 0;   #ifdef CONFIG_SERIAL_BFIN      /* Check for Blackfin Serial */      if (!strncmp(buf, "serial,uart", 11)) {          buf += 11;          early_console = earlyserial_init(buf);      } #endif #ifdef CONFIG_FB          /* TODO: add framebuffer console support */ #endif        if (likely(early_console)) {          early_console->flags |= CON_BOOT;            register_console(early_console);          printk(KERN_INFO "early printk enabled on %s%d/n",               early_console->name,               early_console->index);      }        return 0; } 这段代码在简单判断后,有两个关键的调用。第一个是earlyserial_init,用以初始化串口的硬件参数。第二个调用是register_console,用以注册一个console结构体,同时输出printk缓冲区中的已有数据。

1.2.2   earlyserial_init

这一函数的实现为: static struct console * __init earlyserial_init(char *buf) {      int baud, bit;      char parity;      unsigned int serial_port = DEFAULT_PORT;      unsigned int cflag = DEFAULT_CFLAG;        serial_port = simple_strtoul(buf, &buf, 10);      buf++;        cflag = 0;      baud = simple_strtoul(buf, &buf, 10);      switch (baud) {      case 1200:          cflag |= B1200;