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 》。
这个全局变量用以指定对 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,
};
这个全局变量的定义为:
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
};
这个就是得以使用的 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 。
这个全局变量的定义为:
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,
};
当内核调用此函数时,仅仅做了一些最基本的硬件初始化工作,如 CCLK , EBIU 等。下面看看它的实现:
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 缓冲区中的已有数据。
这一函数的实现为:
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;
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮