Uboot中控制台的前期初始化
来源:互联网 发布:邱泽 知乎 编辑:程序博客网 时间:2024/06/04 23:40
在u-boot完成第一阶段基本的硬件初始化、重定位代码、建立堆栈,清除bss段后,将会跳转到start_armboot中执行.这是u-boot
执行的第一段C语言代码,完成系统的初始化工作,进入主循环,处理用户输入的命令。
在这个初始化过程中,start_armboot首先会根据结构体变量init_sequence[]定义的顺序执行初始化的工作,下面以U-Boot
2009.08-rc1中S3C44B0为例分析与控制台相关的初始化工作。
1 控制台的前期初始化
在start_armboot中执行serial_init初始化串口后,将会调用console_init_f进行控制台前期的初始化操作,这时候标准设备还没有
初始化,于是使用串口作为控制台的输入输出。console_init_f执行如下语句:
gd->have_console = 1;
gd是全局结构体变量,成员have_console=1表明将串口作为控制台的输入输出设备。
2 设备初始化
在完成控制台的前期初始化工作后,将会初始化设备表,并按编译选项注册特定的设备到设备列表中,这里用到一个重
要的结构体变量:
struct stdio_dev {
int flags; /* Device flags:input/output/system */
int ext; /* Supported extensions */
char name[16]; /* Device name */
int (*start) (void); /* To start the device */
int (*stop) (void); /* To stop the device */
void (*putc) (const char c); /* To put a char */
void (*puts) (const char *s); /* To put a string (accelerator) */
void *priv; /* Private extensions */
struct list_head list;
};
struct list_head {
struct list_head *next, *prev;
};
结构体stdio_dev为创建的设备表的类型定义,其成员函数将在下面用到的地方加以说明。这个过程会进入common/stdio.c中调用
stdio_init()函数。
39 static struct stdio_dev devs;
204 int stdio_init (void)
205 {
......
217 /* Initialize the list */
218 INIT_LIST_HEAD(&(devs.list));
219
......
238 drv_system_init ();
239 #ifdef CONFIG_SERIAL_MULTI
240 serial_stdio_init ();
241 #endif
......
252 return (0);
253 }
在218行以devs.list为参数,初始化设备列表。这里的devs.list是一个struct list_head 类型的双向链表,初始化
的操作就是将(devs.list)->next和(devs.list)->prev都指向devs.list本身。初始化设备列表后,将会根据所定义
宏变量将指定的设备作为输入输出设备注册到设备列表中。 默认情况下会调用drv_system_init()函数将串口设备
到devs中。
71 static void drv_system_init (void)
72 {
73 struct stdio_dev dev;
74
75 memset (&dev, 0, sizeof (dev));
76
77 strcpy (dev.name, "serial");
78 dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
......
85 dev.putc = serial_putc;
86 dev.puts = serial_puts;
87 dev.getc = serial_getc;
88 dev.tstc = serial_tstc;
90 stdio_register (&dev);
......
105 }
将串口名,串口的输入输出函数的指针都传递给dev相应的成员变量,并置设备标志dev.flags。接着调用
stdio_register (&dev)函数进行注册。
153 int stdio_register (struct stdio_dev * dev)
154 {
155 struct stdio_dev *_dev;
156
157 _dev = stdio_clone(dev);
158 if(!_dev)
159 return -1;
160 list_add_tail(&(_dev->list), &(devs.list));
161 return 0;
162 }
stdio_clone(dev)函数首先申请sizeof(stdio_dev)大小的动态内存空间并返回起始地址_dev,然后将dev中的
内容拷贝到_dev对应的成员变量中,注册的最后一步激将调用list_add_tail函数,这里所做所做的插入操作是通
过dev的list的prev和next将_dev和初始化的devs链接起来,形成一个双向的循环链表。如下图所示:左上为初始
化后的状态,右上为注册的串口设备的结构体_dev,下面为加入注册后设备列表的情况。至此完成设备的初始化和
设备的注册。
3 控制台的后期初始化
这个过程调用console_init_r()函数,主要完成的工作将扫描设备表中注册的设备,并将扫描到得设备和控制台绑定,
以从特定的设备完成输入输出。下面分析其具体过程:
651 int console_init_r(void)
652 {
......
655 struct list_head *list = stdio_get_list();
......
667
668 /* Scan devices looking for input and output devices */
669 list_for_each(pos, list) {
670 dev = list_entry(pos, struct stdio_dev, list);
671
672 if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
673 inputdev = dev;
674 }
675 if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
676 outputdev = dev;
677 }
678 if(inputdev && outputdev)
679 break;
680 }
682 /* Initializes output console first */
683 if (outputdev != NULL) {
684 console_setfile(stdout, outputdev);
685 console_setfile(stderr, outputdev);
690 }
691
692 /* Initializes input console */
693 if (inputdev != NULL) {
694 console_setfile(stdin, inputdev);
698 }
700 gd->flags |= GD_FLG_DEVINIT;
首先调用stdio_get_list()取得设备列表,返回devs.list的指针。接着调用宏list_for_each(pos, list),它是
一个for循环
193 #define list_for_each(pos, head) \
194 for (pos = (head)->next, prefetch(pos->next); pos != (head); \
195 pos = pos->next, prefetch(pos->next))
prefetch(x)为1不用考虑,这里就是根据devs.list扫描设备表,因为前面在注册设备时也是通过devs.list的prev
和next添加设备到设备列表的。进入循环后调用list_entry()将pos当前地址减去list字段在devs中的偏移量得到dev
的起始地址,接着判断扫描到的设备的flags字段,如果置了输入输出标志则将扫描到的设备作为输入输出设备。输入输
出设备找到后退出循环,否则将扫描整个设备列表都没有找到可用的设备为止。
当找到可用的输入输出设备后,程序将会执行console_setfile()函数,将搜到的设备指针赋给标准I/O数组,完成控制
台的初始化。
77 switch (file) {
78 case stdin:
79 gd->jt[XF_getc] = dev->getc;
80 gd->jt[XF_tstc] = dev->tstc;
81 break;
82 case stdout:
83 gd->jt[XF_putc] = dev->putc;
84 gd->jt[XF_puts] = dev->puts;
85 gd->jt[XF_printf] = printf;
86 break;
87 }
88 break;
89
最后置gd->flag标志GD_FLG_DEVINIT。这个标志影响putc,getc函数的实现,未定义此标志时直接由串口
serial_getc,serial_putc实现,定义以后通过标准设备数组中的 putc和getc来实现IO。
因此使用devlist,标准I/O可以更灵活地实现标准IO重定向,任何可以作为标准IO的设备, 都可以对应一个stdio_dev的结构体变
量,只需要实现getc和putc等函数,就能加入到设备列表中去, 也就可以被assign为标准IO设备数组中去。
如函数 int console_assign (int file, char *devname); 这个函数功能就是把名为devname的设备重定向为标准IO文件file,其执行
过程是在设备列表中查找devname的设备,返回这个设备的stdio_dev指针,并把指针值赋给标准I/O数组中。
- Uboot中控制台的前期初始化
- uboot中控制台输入输出的实现
- 从零开始-uboot的移植-前期准备:uboot是如何启动倒数,启动内核的
- uboot下的DRAM的初始化
- 软件开发中前期准备的重要性
- uboot的eMMC初始化代码流程分析
- TI的DM3730之dvsdk中uboot时钟配置初始化深入解析
- imx515 uboot 改变控制台
- uboot初始化中为什么要设置CPU为SVC模式
- bootloader---27.uboot中SD初始化及读写分析
- 4(1)、uboot中系统时钟初始化函数:system_clock_init
- 4(2)、uboot中内存初始化函数:mem_ctrl_asm_init
- vs中控制台应用程序 main函数中 初始化args
- uboot初始化分析
- uboot 串口初始化
- uboot串口初始化
- uboot之flash初始化
- Uboot中 TEXT_BASE的理解
- oracle 分区表exchange原理
- tar.tar 后缀文件的解压方法
- 设计模式学习笔记-状态模式
- C++ FAQ学习笔记 22章 继承 — 抽象基类(ABCs)
- 服务器临时存储数据
- Uboot中控制台的前期初始化
- uboot load address、entry point、 bootm address以及kernel运行地址的意义及联系
- 双向链表
- uImage
- CityEngine ESRI专业技术博客(转)
- 【解决方法】System.IO.FileNotFoundException
- 禁止页面复制功能 js禁止复制 禁用页面右键菜单
- android 解析json数据格式
- 最临近插值和双线性内插值算法实现比较