uboot2010.03关于LCD输出的处理-printf()函数分析

来源:互联网 发布:app开发编程工具哪个好 编辑:程序博客网 时间:2024/05/17 06:07

printf()函数定义在common/console.c中,是通用函数,除了特殊情况外移植时不需要改动。

void printf(const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer[CONFIG_SYS_PBSIZE];


va_start(args, fmt);


/* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vsprintf(printbuffer, fmt, args);
va_end(args);


/* Print the string */
puts(printbuffer);
}

1> 函数中形参列表和va_list与c语言变参函数的处理有关,这里不在讨论内容之中,本文讨论uboot关于printf函数输出的内部处理方式,从整体上考虑。

2> vsprintf()为参数格式处理函数,在lib_generic/vsprintf.c中定义。

3> 具体的输出函数为puts()在common/console.c中定义:

void puts(const char *s)
{
#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->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fputs(stdout, s);
} else {
/* Send directly to the handler */
serial_puts(s);
}
}

两个宏定义是禁止输出的宏定义。从函数的主体可以看到,gd->flags & GD_FLG_DEVINIT,如果设备标准输入输出设备已经初始化,输出时,函数调用的是标准输出设备,如果没有,则使用串口输出函数。当然,标准输出设备也可能是串口,这时调用的是同一个函数,但是也有可能是LCD,主要看自己的设置。

我用的开发板是mini2440,相关串口处理的函数都定义在driver/serial/serial_s3c24x0.c中,对于串口大家还是比较熟悉的,这里不在多数,有兴趣的可以自己到该文件中自己查看。

下面我们来看一下,系统标准设备的处理。

fputs()函数定义在common/console.c中:

void fputs(int file, const char *s)
{
if (file < MAX_FILES)
console_puts(file, s);
}

static inline void console_puts(int file, const char *s)
{
stdio_devices[file]->puts(s);
}

stdio_devices为结构体指针数组:struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL };

结构体定义如下:

struct stdio_dev {
int flags;/* Device flags: input/output/system */
int ext;/* Supported extensions */
char name[16];/* Device name */


/* GENERAL functions */


int (*start) (void);/* To start the device */
int (*stop) (void);/* To stop the device */


/* OUTPUT functions */


void (*putc) (const char c);/* To put a char */
void (*puts) (const char *s);/* To put a string (accelerator) */


/* INPUT functions */


int (*tstc) (void);/* To test if a char is ready... */
int (*getc) (void);/* To get that char */


/* Other functions */


void *priv; /* Private extensions */
struct list_head list;
};

该结构体定义了标准设备的结构,包含一些指示标志和输入输出函数的指针,总体看该结构体还是很紧密的。

结构体有了,下面我们分析一下标准设备结构体指针数组的确定。

标准设备一种有3个,标准输入设备,标准输出设备,标准错误输出设备。uboot中常用的标准设备主要有串口和LCD,其中串口可以作为这三种设备的任何一个,LCD设备因为没有输入功能(触摸屏单独考虑),只能作为标准输出和标准错误设备。

标准设备结构体指针数组的确定在函数console_init_r ()中:

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);
}


#if 0
/* If nothing usable installed, use only the initial console */
if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL))
return 0;
#endif


return 0;
}

在系统的设备链表中根据设备的标志(flags)属于输出设备还是输入设备,将他们选择到标准输入输出设备指针数组中。具体的执行函数为:

static int console_setfile(int file, struct stdio_dev * dev)
{
int error = 0;


if (dev == NULL)
return -1;


switch (file) {
case stdin:
case stdout:
case stderr:
/* Start new device */
if (dev->start) {
error = dev->start();
/* 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[XF_getc] = dev->getc;
gd->jt[XF_tstc] = dev->tstc;
break;
case stdout:
gd->jt[XF_putc] = dev->putc;
gd->jt[XF_puts] = dev->puts;
gd->jt[XF_printf] = printf;
break;
}
break;


default: /* Invalid file ID */
error = -1;
}
return error;
}

现在标准输入输出设备结构体确定了,现在再来分析系统设备链表又是怎么填充或者初始化的。

系统设备链表初始化是在函数common/stdio.c:stdio_init()中完成的,那么该函数在整个uboot中所处的位置是什么呢?

_start->start_armboot()->stdio_init()

int stdio_init (void)
{
#if !defined(CONFIG_RELOC_FIXUP_WORKS)
/* already relocated for current ARM implementation */
ulong relocation_offset = gd->reloc_off;
int i;


/* relocate device name pointers */
for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
relocation_offset);
}
#endif /* !CONFIG_RELOC_FIXUP_WORKS */


/* Initialize the list */
INIT_LIST_HEAD(&(devs.list));


#ifdef CONFIG_ARM_DCC_MULTI
drv_arm_dcc_init ();
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
#ifdef CONFIG_LCD
drv_lcd_init ();
#endif
#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)
drv_video_init ();
#endif
#ifdef CONFIG_KEYBOARD
drv_keyboard_init ();
#endif
#ifdef CONFIG_LOGBUFFER
drv_logbuff_init ();
#endif
drv_system_init ();
#ifdef CONFIG_SERIAL_MULTI
serial_stdio_init ();
#endif
#ifdef CONFIG_USB_TTY
drv_usbtty_init ();
#endif
#ifdef CONFIG_NETCONSOLE
drv_nc_init ();
#endif
#ifdef CONFIG_JTAG_CONSOLE
drv_jtag_console_init ();
#endif


return (0);
}

可以看到,这里有很多设备的初始化函数。其中有一个函数为:drv_system_init()该函数就是串口设备的系统设备链表的初始化函数:

static void drv_system_init (void)
{
struct stdio_dev dev;


memset (&dev, 0, sizeof (dev));


strcpy (dev.name, "serial");
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
#ifdef CONFIG_SERIAL_SOFTWARE_FIFO
dev.putc = serial_buffered_putc;
dev.puts = serial_buffered_puts;
dev.getc = serial_buffered_getc;
dev.tstc = serial_buffered_tstc;
#else
dev.putc = serial_putc;
dev.puts = serial_puts;
dev.getc = serial_getc;
dev.tstc = serial_tstc;
#endif


stdio_register (&dev);


#ifdef CONFIG_SYS_DEVICE_NULLDEV
memset (&dev, 0, sizeof (dev));


strcpy (dev.name, "nulldev");
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
dev.putc = nulldev_putc;
dev.puts = nulldev_puts;
dev.getc = nulldev_input;
dev.tstc = nulldev_input;


stdio_register (&dev);
#endif
}

从上述函数中可以很清楚的看到,初始化的过程,最后使用函数stdio_register()完成设备的注册。

drv_video_int()函数实现了LCD设备的注册:

int drv_video_init (void)
{
int skip_dev_init;
struct stdio_dev console_dev;


/* Check if video initialization should be skipped */
if (board_video_skip())
return 0;


/* Init video chip - returns with framebuffer cleared */
skip_dev_init = (video_init () == -1);


#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE)
PRINTD ("KBD: Keyboard init ...\n");
skip_dev_init |= (VIDEO_KBD_INIT_FCT == -1);
#endif


if (skip_dev_init)
return 0;


/* Init vga device */
memset (&console_dev, 0, sizeof (console_dev));
strcpy (console_dev.name, "vga");
console_dev.ext = DEV_EXT_VIDEO;/* Video extensions */
console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM;
console_dev.putc = video_putc;/* 'putc' function */
console_dev.puts = video_puts;/* 'puts' function */
console_dev.tstc = NULL;/* 'tstc' function */
console_dev.getc = NULL;/* 'getc' function */


#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE)
/* Also init console device */
console_dev.flags |= DEV_FLAGS_INPUT;
console_dev.tstc = VIDEO_TSTC_FCT;/* 'tstc' function */
console_dev.getc = VIDEO_GETC_FCT;/* 'getc' function */
#endif /* CONFIG_VGA_AS_SINGLE_DEVICE */


if (stdio_register (&console_dev) != 0)
return 0;


/* Return success */
return 1;
}

这里要说明一点的是,该函数在设备注册之前要完成硬件的初始化,然后才进行设备注册,而上一个函数则直接进行注册,因为由于串口是最基本的交互设备在启动时需要串口输出一些信息,硬件初始化在前面已经完成,在此直接注册就可以。

至此printf()函数的分析告一段落,在分析的过程中,我们不仅完成了printf()函数实现机理的分析,同时也将uboot关于硬件设备的管理也一定程度上分析了一部分。我们分析的重点并不是代码的实现细节和技巧上,而是解决问题的思路和实现层次上。整体把握对象才能从本质上了解对象。

0 0
原创粉丝点击