[uboot] (番外篇)uboot串口&console&stdio设备工作流程
来源:互联网 发布:织梦if标签 编辑:程序博客网 时间:2024/06/11 18:14
[uboot] uboot流程系列:
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)
[project X] tiny210(s5pv210)从存储设备加载代码到DDR
[uboot] (第一章)uboot流程——概述
[uboot] (第二章)uboot流程——uboot-spl编译流程
[uboot] (第三章)uboot流程——uboot-spl代码流程
[uboot] (第四章)uboot流程——uboot编译流程
[uboot] (第五章)uboot流程——uboot启动流程
[uboot] (番外篇)global_data介绍
[uboot] (番外篇)uboot relocation介绍
[uboot] (番外篇)uboot 驱动模型
建议先看《[uboot] (番外篇)uboot 驱动模型》
=============================================================================================================
一、uboot serial框架
1、serial模块驱动模型
在《[uboot] (番外篇)uboot 驱动模型》中我们已经介绍uboot的驱动模型,uboot DM。
在uboot中,serial模块也使用了对应的驱动模型。
其框架图如下:
我们在《[uboot] (番外篇)uboot 驱动模型》已经说明过了,这里再简单解释一下:
- serial core为serial模块向外提供接口,但是也是在serial-uclass中实现
- serial uclass是serial设备的集合抽象,为serial设备提供统一的操作接口,serial uclass driver则是其对应的驱动
- serial udevice是serial设备的具体抽象,代表了一个serial设备对象,serial driver则是其对应的驱动
2、serial DM实现
在《[uboot] (番外篇)uboot 驱动模型》中,我们已经知道了uclass和udevice由uboot动态生成,但是我们需要在dtsi中添加相应的设备信息,以及添加相应的uclass driver和udevice driver.
以tiny210为例,如下:
- dts中的设备信息
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
这里,有些人或许会有疑问,在relocate之前就需要打印串口数据,就需要使用到这个节点了,为什么不需要加上“u-boot,dm-pre-reloc”属性?
确实是可以加,但是不加也没事,因为console中已经指定了串口节点的路径,在relocate之前的串口初始化过程中,在设备链表上找不到对应串口设备的话,会强制绑定console指定的串口节点的设备。
- uclass driver
driver/serial/serial-uclass.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- udevice driver
driver/serial/serial_s5p.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
注意,serial uclass driver的id和serial driver的id是一致的,都是UCLASS_SERIAL。
具体也在《[uboot] (番外篇)uboot 驱动模型》分析过了,这里也不多说了。
关于tiny210的串口驱动的实现,在第四节中再学习。
3、serial core提供的接口
serial core会利用serial uclass找到对应的设备及其操作集,向上层提供接口。
如下:
driver/serial/serial-uclass.c
- void serial_putc(char ch)
往gd->cur_serial_dev指定的串口设备输出一个字符。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
void serial_puts(const char *str)
往gd->cur_serial_dev指定的串口设备输出字符串。其方法与serial_putc类似,自己参考代码。int serial_getc(void)
从gd->cur_serial_dev指定的串口设备获取一个字符。其方法与serial_putc类似,自己参考代码。int serial_tstc(void)
判断gd->cur_serial_dev指定的串口设备是否有数据在等待。其方法与serial_putc类似,自己参考代码。void serial_setbrg(void)
设置gd->cur_serial_dev指定的串口设备的波特率。其方法与serial_putc类似,自己参考代码。void serial_initialize(void) & int serial_init(void)
串口初始化。这里重点说明
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
在串口初始化完成之后,如果指定了console并且对应serial节点正常,那么serial_putc等等API都可以正常使用了。
同时,gd->flags中的GD_FLG_SERIAL_READY的标志也被设置了
后续就可以通过serial_putc、serial_puts、serial_getc向串口输出数据或者获取数据了。
4、默认波特率的设置
有两个地方可以设置默认波特率,并且存储到gd->baudrate中
- 环境变量”baudrate”
- 如果环境变量”baudrate”不存在,则使用CONFIG_BAUDRATE宏
具体参考代码:
common/board_f.c
- 1
- 2
- 3
- 4
- 5
- 6
在include/configs/tiny210.h中配置默认波特率如下:
- 1
5、serial初始化时机
在relocate之前的board_f中和relocate之后的board_r各会执行一次串口初始操作
- 在relocate之前的serial初始化
common/board_f.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 在relocate之后的serial初始化
common/board_r.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
二、debug、printf的输出流程
我们知道,在uboot中,可以通过debug和printf两个函数来进行串口log输出。
1、简单流程图
简单说明如下
- debug会转化为printf
- printf会调用到console的puts接口进行输出
- console的puts的输出主要分成如下三个阶段
- 在serial初始化之前,会存储到pre console buffer中
- 在serial初始化之后,console完全初始化(console_init_r)之前,会直接调用serial的serial_puts接口进行输出
- 在serial初始化之后,console完全初始化(console_init_r)之后,会使用标准输入输出设备进行输出,但最终也是会调用到串口设备进行输出
2、debug()流程
- 要使能debug()的输出功能,需要先打开DEBUG宏
include/common.h
- 1
- 2
- 3
- 4
- debug()使用方法如下:
- 1
- 2
- debug()定义如下:
include/common.h
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
可以观察到最终也是调用到printf进行输出。
3、printf()流程
printf代码流程如下:
lib/vsprintf.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
几个重点标志位说明一下:
gd->have_console
用于判断是否有console,在relocate之前、serial串口初始化之后的console_init_f进行设置,
这里可以简单的理解为作为console的串口初始化的标识gd->flags & GD_FLG_DEVINIT
标准输入输出设备初始化完成的标识。
在console_init_r中设置,当这个标识被设置,就表示console已经完全初始化了。
4、pre console buffer的使用
根据上面第2小节说明,在serial初始化之前,发到串口的数据会通过pre_console_puts存储到pre console buffer中。
当串口初始化之后,在console_init_f中会对pre console buffer里面的内容进行输出
- 需要打开的宏
以tiny210为例
include/configs/tiny210.h
- 1
- 2
- 3
- 存储流程
在puts中通过调用pre_console_puts将字符串存储到pre console buffer中,代码流程如下:
common/console.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 输出流程
uboot会在relocate之前的串口初始化之后,通过调用console_init_f对pre console buffer的内容进行输出。代码如下:
common/console.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
三、标准输入输出(stdio)
前面也说了,当console完全初始化之后,会生成标准输入输出设备。
后续会在puts中调用fputs(stdout, s)对串口数据进行输出。
1、标准输入输出设备的结构体
include/stdio_dev.h
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
可以看到包含了标准输入输出设备的一些操作集。
2、标准输入输出设备的存储位置
uboot创建了一个虚拟stdio设备devs作为所有stdio设备的链表头,所有其他stdio设备都会挂载到devs.list链表上。
并且提供了如下操作链表的方法:
common/stdio.c
- 1
- 2
- 3
- 4
- 5
- 6
- 7
并且默认提供了stdin、stdout、stderr三个标准输入输出设备
- 1
- 2
3、stdio模块初始化
stdio模块的初始化是在relocate之后的board_r中实现的
- 1
- 2
- 3
- 4
- 5
stdio_init_tables用于stdio链表的初始化
stdio_add_devices用于创建一些标准输入输出设备。
console_init_r用于将console和标准输入输出设备关联
4、stdio_add_devices
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
经过上述步骤之后就注册了serial的标准输入输出设备了。
5、console_init_r
console_init_r主要用将console和标准输入输出设备关联。
其会获取stdio链表中的第一个满足条件的标准输入输出设备,进行关联。
代码如下
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
后续就可以通过fputs(stdout, s)从串口输出字符,或者通过fgets(string, sizeof(string), stdin);从串口中获取字符。
6、fputs流程
fputs也是有console实现,较为简单,简单地看一下代码
fputs(stdout, s)从串口输出的流程如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
到这里,框架层的串口输出流程就分析完成了,也就是板级无关的部分。
后面我们分析一下和板级相关的串口驱动的部分。
四、s5pv210 seriial driver分析
和tiny210(s5pv210)相关性较强,这里仅仅说明一下思路。
代码具体参考driver/serial/serial_s5p.c
1、先定义驱动中需要使用到的私有数据
- 1
- 2
- 3
- 4
- 5
2、定义DM模型中的driver结构体
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
3、设置match_id:s5p_serial_ids
- 1
- 2
- 3
- 4
4、设置dts节点的解析函数:s5p_serial_ofdata_to_platdata
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
5、实现probe函数:s5p_serial_probe
probe就相当于是激活这个串口,就需要在probe中对这个串口进行初始话
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
6、实现操作集:s5p_serial_ops
注意,因为这个driver对应的udevice所属的serial uclass类型,因此其操作集的类型必须定义为dm_serial_ops类型。
- 1
- 2
- 3
- 4
- 5
- 6
具体实现这里不多说了。
7、在dtsi中添加设备信息
- [uboot] (番外篇)uboot串口&console&stdio设备工作流程
- [uboot] (番外篇)uboot串口&console&stdio设备工作流程
- [uboot] (番外篇)uboot dm-gpio使用方法以及工作流程
- [uboot] (番外篇)uboot dm-gpio使用方法以及工作流程
- uboot工作流程分析
- 二十一.UBOOT工作流程
- uboot配置、编译、工作流程
- UBOOT 学习心得(UBOOT流程分析)
- UBOOT 学习心得(UBOOT流程分析)
- uboot流程
- [uboot] (第二章)uboot流程——uboot-spl编译流程
- [uboot] (第三章)uboot流程——uboot-spl代码流程
- [uboot] (第四章)uboot流程——uboot编译流程
- [uboot] (第五章)uboot流程——uboot启动流程
- [uboot] (第二章)uboot流程——uboot-spl编译流程
- [uboot] (第三章)uboot流程——uboot-spl代码流程
- [uboot] (第四章)uboot流程——uboot编译流程
- [uboot] (第五章)uboot流程——uboot启动流程
- Java关键词final和static
- c/c++文件处理
- HBase-数据写入流程解析
- C语言每周三道题11.18
- INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113
- [uboot] (番外篇)uboot串口&console&stdio设备工作流程
- JavaScript 六种继承方式
- sql语句的执行顺序
- 浅谈条件语句if结构
- 关于C#字符串按照小括号拆分报错问题
- Django之模板继承与ajax使用错误处理~
- Java Jsp JavaBean组件
- [uboot] (番外篇)uboot dm-gpio使用方法以及工作流程
- Class requires API level 7 (current min is 1)----Android