1. 前言
对Linux kernel工程师来说,最依赖的工具非printk莫属(不多解释,大家都懂)。因此,在Linux kernel移植的初期阶段,如果能够尽快地实现printk功能,将会为后续的工作带来极大的帮助。
在众多可用作printk输出的终端里面(串口、屏幕、USB、网络、等等),串口终端(也即串口驱动)无疑是实现起来最简单一种,因此也是嵌入式linux开发过程中(特别是早期阶段)最普遍使用的。
但是,受限于Linux TTY框架的复杂性 [1] ,长久以来,在Kernel移植的初期阶段(各种功能都不ready,缺乏有效的调试手段),快速的实现serial driver也是一个不小的挑战。不过,随着serial subsystem中的early console功能的出现,这种状况得到了极大的改善。
本文将借助“X Project” kernel的开发过程,介绍serial early console功能的移植过程。
注1:博客中“Linux TTY子系统 ”的分析正在缓慢推进,不过还未涉及console、serial subsystem、earlycon等模块。因此我一直在纠结先写理论(分析),还是先写实践(移植说明)。结论是先写本文,理由是:虽然earlycon功能涉及到TTY子系统的复杂知识,如console driver、tty driver、serial driver等等,但从移植的角度看,我们可以什么都不懂。这种优雅的抽象和封装,是优秀软件(如Linux kernel)所必需具备的特性,也是我们软件人需要竭力追求的神圣目标。
2. 移植步骤
2.1 串口驱动
early console是linux serial subsystem的一个子功能,因此需要依附于具体平台的serial driver之下(放心,本文不涉及任何串口驱动的知识)。以“X Project” 所使用的bubblegum-96平台为例,我们需要在“drivers/tty/serial/”下新建一个serial driver,并修改kernel serial subsystem的Kconfig和Makefile文件,将其添加到kernel的编译系统中, 步骤如下:
1)新建serial driver
touch drivers/tty/serial/ owl-serial.c
其中“owl”是bubblegum-96平台所使用SOC的代号,大家可以根据实际情况,使用和自己平台匹配的名称。
2)修改drivers/tty/serial/Kconfig和drivers/tty/serial/Makefile,将新建的serial driver加入到编译框架中
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 13d4ed6..bbf4b69 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1624,6 +1624,14 @@ config SERIAL_MVEBU_CONSOLE
and warnings and which allows logins in single user mode)
Otherwise, say 'N'.
+config SERIAL_OWL
+ tristate "Actions OWL serial port support"
+ depends on ARCH_OWL
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ help
+ If you have a machine based on an Actions OWL CPU you
+ can enable its onboard serial ports by enabling this option.
endmenuconfig SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 8c261ad..3f75d73 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o
obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o
+obj-$(CONFIG_SERIAL_OWL) += owl-serial.o
我们为新建的serial driver指定了“SERIAL_OWL”配置项,该配置项依赖ARCH_OWL [2]。 与此同时,我们需要选中“SERIAL_CORE”和“SERIAL_CORE_CONSOLE”两个配置项,以支持earlycon功能。
3)配置kernel,开启TTY、serial等功能,并使能我们新加入的serial driver
cd ~/work/xprj/build
make kernel-config
#选中如下的配置项
Device Drivers --->
Character devices --->
[*] Enable TTY
Serial drivers --->
[*] Actions OWL serial port support
完成后make kernel重新编译即可。
2.2 early console的移植
实现early console的移植过程非常简单,包括:
1)实现一个early console的setup接口,并调用serial core提供的注册接口(EARLYCON_DECLARE)将其注册到kernel中,如下:
int __init earlycon_owl_setup(struct earlycon_device *device, const char *opt)
{
/* TODO */
uart5_base = early_ioremap(UART5_BASE, 4);
device->con->write = earlycon_owl_write;
return 0;
}
EARLYCON_DECLARE(owl_serial, earlycon_owl_setup);
其中“owl_serial”是early console的名称,earlycon_owl_setup是开始使用之前的初始化接口,需要在这个初始化接口中做两件事情:
进行一些必要的初始化,如例子中的寄存器map;
为printk输出制定一个write接口----earlycon_owl_write。
注2:“EARLYCON_DECLARE”是实现early console的一种方法,另外我们也可以使用device tree的方式,为了不增加复杂度,本文就不提及相关的实现了。
注3:为了简单,early console和u-boot [3] 使用相同的串口,并且使用相同的配置,因此不需要额外的初始化操作。
注4:虽然配置不变,我们还是需要访问串口寄存器输出字符串,因此需要map相应的IO地址,由于early console需要在很早的时候使用,此时mm还没有初始化,因此我们可以用early_ioremap [4] 进行map。
2)earlycon_owl_write的实现
console输出需要字符串处理,可以借用serial core的标准接口----uart_console_write:
static void earlycon_owl_write(struct console *con, const char *s, unsigned n)
{
/* TODO */
uart_console_write(NULL, s, n, owl_serial_putc);
}
我们需要做的就是提供一个字符输出的API----owl_serial_putc,具体可参考代码以及“ X-004-UBOOT-串口驱动移植(Bubblegum-96平台) [3] ”中有关的描述。
3)移植完成后重新编译kernel即可
3. early console的使用
移植完成后,可以通过kernel的命令行参数,告诉kernel在启动的时候使用该early console,参数的格式如下:
earlycon= owl_serial
其中“earlycon”是关键字,“ owl_serial ”是early console的名字。
命令行参数可以通过u-boot传入,为了测试方便,可以暂时加到kernel的配置项中 [5] ,如下:
#
# Boot options
#
-CONFIG_CMDLINE=""
+CONFIG_CMDLINE="earlycon=owl_serial"
以上改动具体可参考下面的patch:
https://github.com/wowotechX/linux/commit/1285ab5e6e5c5bb263a1d715fe04cca2d217bd98
4. 参考文档
[1] Linux TTY
[2] X-009-KERNEL-Linux kernel的移植(Bubblegum-96平台)
[3] X-004-UBOOT-串口驱动移植(Bubblegum-96平台)
[4]Fix-Mapped Addresses