/***************初始化输入输出***********************/
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
//获取环境变量ip地址
stdio_init ();
/* get the devices list going. */
//标准输入输出的初始化
jumptable_init ();
//跳转表,在之后的控制台初始化时会用到,存放设备的各种操作等
stdio_init 在common/stdio.c中定义:
int stdio_init (void)
{
/* Initialize the list */
INIT_LIST_HEAD(&(devs.list));
//创建一个设备链表,变量devs就在common/stdio.c中定义。是一个
struct stdio_dev
//结构体的变量。这个结构体就是定义了一系列操作设备的函数指针,另外还包含了设
//
备名
称,标记和私有数据等等。这个链表中存放了所有用到的设备 ,也就是下面
//所有被初始化过的设备
/*************下面是对各个用到的设备的初始化,并插入到devs链表中*********************/
#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 ();
//对LCD初始化
#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);
}
以上对各个设备做的初始化步骤大同小异,基本就是给每个设备的struct stdio_dev 结构体赋值,定义一系列的对这个设备的操作,包括输入输出等。设置名字等。最后都会调用 stdio_register 函数,复制一份结构体。然后插入到devs链表中。
下面只对串口标准输入输出的初始化简单注释:
serial_stdio_init 函数定义在common/serial.c中:
void serial_stdio_init (void)
{
struct stdio_dev dev;
struct serial_device *s = serial_devices;
//将已经初始化好的串口设备结构体指针赋给s。
while (s) {
//循环将所有的串口设备初始化给标准输入输出
memset (&dev, 0, sizeof (dev)); //清空dev结构体变量
strcpy (dev.name, s->name); //将串口设备的名字赋给dev结构体中的name项
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; //设置标志位,表明该串口时标准输入输出
dev.start = s->init;
//给该串口设备进行一系列的操作函数赋值
dev.putc = s->putc;
dev.puts = s->puts;
dev.getc = s->getc;
dev.tstc = s->tstc;
stdio_register (&dev);
//向标准输入输出注册设备
s = s->next;
//指向下一个串口设备
}
}
stdio_register 函数在common/stdio.c中定义:
int stdio_register (struct stdio_dev * dev)
{
struct stdio_dev *_dev;
_dev = stdio_clone(dev);
//复制一份该设备的dev结构
if(!_dev)
return -1;
list_add_tail(&(_dev->list), &(devs.list));
//将复制后的dev结构插入到链表中,就是先前INIT_LIST_HEAD(&(devs.list));
return 0;
//创建的链表
}
/****************控制台初始化********************/
console_init_r ();
console_init_r有两种实现,一种是从环境变量中获取控制台信息,这个代码比较容易看懂,不做分析。第二个是从stdio_init中创建好的devs链表中获取控制台信息,下面的代码就是基于这种方式实现的控制台初始化。
console_init_r 函数定义在common/console.c中:
int console_init_r(void)
{
struct stdio_dev *inputdev = NULL, *outputdev = NULL;
int i;
struct list_head *list = stdio_get_list();
//获取devs链表的地址
struct list_head *pos;
struct stdio_dev *dev;
/* Scan devices looking for input and output devices */
list_for_each(pos, list) {
//list_for_each就是一个for循环的宏,用来便利devs链表
dev = list_entry(pos, struct stdio_dev, list);
if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
//通过dev->flags判断该设备是否可以被作为标准输入
inputdev = dev;
//如果可以,则将这个设备的struct stdio_dev结构指针赋给inputdev 。
}
if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
//通过dev->flags判断该设备是否可以被作为标准输出
outputdev = dev;
//如果可以,则将这个设备的struct stdio_dev结构指针赋给inputdev
}
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;
//标记设备初始化已经完成
stdio_print_current_devices();
//打印输入输出信息,就是uboot刚上电时打印的那段信息,此函数很简单,不作分析
// In: serial
// Out: serial
// Err: serial
/* Setting environment variables */
for (i = 0; i < 3; i++) {
setenv(stdio_names[i], stdio_devices[i]->name);
//设置环境变量
}
return 0;
}
console_setfile函数定义在common/console.c中:
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;
//将这个设备的struct stdio_dev结构体指针放到这个数组中,这个数组中存放了
//标准输入输出以及出错的设备的
struct stdio_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.h中定义
:
struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL };
char *stdio_names[MAX_FILES] = { "stdin", "stdout", "stderr" };