对于我们的android平台,控制台被定义到了串口1上,因此初始化过程就是把控制台的输出配置到串口1上

对kernel控制台初始化是在挂载文件系统之前,由于没有串口的设备文件,不能通过打开设备文件来访问串口,只能直接访问硬件,更类似与裸机的访问方式。

下面正式来看

板子初始化的过程

android\kernel_imx\arch\arm\mach-mx6\board-mx6q_sabresd.c

MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
/* Maintainer: Freescale Semiconductor, Inc. */
.boot_params = MX6_PHYS_OFFSET + 0x100,
.fixup = fixup_mxc_board,
.map_io = mx6_map_io,
.init_irq = mx6_init_irq,
.init_machine = mx6_sabresd_board_init,
.timer = &mx6_sabresd_timer,
.reserve = mx6q_sabresd_reserve,
MACHINE_END

这其中有个时钟初始化mx6_sabresd_timer我们来看它的定义

static struct sys_timer mx6_sabresd_timer = {
.init = mx6_sabresd_timer_init,
};
static void __init mx6_sabresd_timer_init(void)
{
struct clk *uart_clk;
#ifdef CONFIG_LOCAL_TIMERS
twd_base = ioremap(LOCAL_TWD_ADDR, SZ_256);
BUG_ON(!twd_base);
#endif
mx6_clocks_init(32768, 24000000, 0, 0); uart_clk = clk_get_sys("imx-uart.0", NULL);
early_console_setup(UART1_BASE_ADDR, uart_clk);
}

可以看到这里调用了early_console_setup(UART1_BASE_ADDR, uart_clk);

这个函数就是文件系统挂载之前控制台的初始化函数。下面我就开始分析这个函数

android\kernel_imx\arch\arm\plat-mxc\cpu.c

/**
* early_console_setup - setup debugging console
*
* Consoles started here require little enough setup that we can start using
* them very early in the boot process, either right after the machine
* vector initialization, or even before if the drivers can detect their hw.
*
* Returns non-zero if a console couldn't be setup.
* This function is developed based on
* early_console_setup function as defined in arch/ia64/kernel/setup.c
* 这个注释里写的很清楚,在设备驱动执行之前,为了调试错误的需要我们
* 需要在启动的最初就初始化控制台
*/
void __init early_console_setup(unsigned long base, struct clk *clk)
{
#ifdef CONFIG_SERIAL_IMX_CONSOLE
mxc_early_serial_console_init(base, clk);
#endif
}
这里调用mxc_early_serial_console_init(base, clk);
android\kernel_imx\drivers\tty\serial、mxc_uart_early.c
int __init mxc_early_serial_console_init(unsigned long base, struct clk *clk)
{
mxc_early_device.clk = clk;
mxc_early_device.port.mapbase = base; register_console(&mxc_early_uart_console);
return 0;
}

这里可以看到register_console(&mxc_early_uart_console);就是注册一个设备到控制台中,

在最开始注册的这个设备肯定是裸机的访问方式的,因此我们重点来看这个设备

static struct console mxc_early_uart_console __initdata = {
.name = "ttymxc",
.write = early_mxcuart_console_write,
.setup = mxc_early_uart_setup,
.flags = CON_PRINTBUFFER | CON_BOOT,
.index = -1,
};

这个设备提供的设备访问接口

.write = early_mxcuart_console_write,是串口的发送函数

.setup = mxc_early_uart_setup,是串口的初始化函数

.flags = CON_PRINTBUFFER | CON_BOOT,是控制台标志,CON_BOOT表明这事一个boot的控制台设备

也就是说是挂载设备文件之前的控制台设备

下面我们来分析初始化函数和 数据发送函数

初始化函数

static int __init mxc_early_uart_setup(struct console *console, char *options)
{ struct mxc_early_uart_device *device = &mxc_early_device;
struct uart_port *port = &device->port;
int length; if (device->port.membase || device->port.iobase)
return -ENODEV; /* Enable Early MXC UART Clock */
clk_enable(device->clk);//初始化总线时钟 port->uartclk = 5600000;
port->iotype = UPIO_MEM;
port->membase = ioremap(port->mapbase, SZ_4K);//串口寄存器内存映射 if (options) {
device->baud = simple_strtoul(options, NULL, 0);
length = min(strlen(options), sizeof(device->options));
strncpy(device->options, options, length);
} else {
device->baud = probe_baud(port);
snprintf(device->options, sizeof(device->options), "%u",
device->baud);
}
printk(KERN_INFO
"MXC_Early serial console at MMIO 0x%x (options '%s')\n",
port->mapbase, device->options);
return 0;
}

其实从这个初始化函数里看出,它做了很多向mxc_early_device结构体中填入数据的工作,而这些数据

找遍所有代码也没有用到,因此这些事没有意义的,官方代码给的这点并不太好。但是由于uboot中我们已经初始化了

串口因此这里就算没有任何初始化其实串口也可以是使用。

这里真正有用的就两句话

clk_enable(device->clk);//初始化总线时钟

port->membase = ioremap(port->mapbase, SZ_4K);//串口寄存器内存映射

但是在寄存器映射结束后没有进行任何串口寄存器初始化,这也很奇怪,我们仔细查找发现,

寄存器初始化代码写在了数据发送函数里,具体为什么我们来分析发送函数

early_mxcuart_console_write

/*!
* This function is called to write the console messages through the UART port.
*
* @param co the console structure
* @param s the log message to be written to the UART
* @param count length of the message
*/
void __init early_mxcuart_console_write(struct console *co, const char *s,
u_int count)
{
struct uart_port *port = &mxc_early_device.port;
unsigned int status, oldcr1, oldcr2, oldcr3, cr2, cr3; /*
* First save the control registers and then disable the interrupts
*/
oldcr1 = readl(port->membase + MXC_UARTUCR1); //读取当前三个串口控制寄存器的值
oldcr2 = readl(port->membase + MXC_UARTUCR2);
oldcr3 = readl(port->membase + MXC_UARTUCR3);
cr2 =
oldcr2 & ~(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_RTSEN | //初始化串口寄存器数值
MXC_UARTUCR2_ESCI);
cr3 =
oldcr3 & ~(MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI |
MXC_UARTUCR3_DTRDEN);
writel(MXC_UARTUCR1_UARTEN, port->membase + MXC_UARTUCR1); //使能串口
writel(cr2, port->membase + MXC_UARTUCR2); //配置寄存器
writel(cr3, port->membase + MXC_UARTUCR3); /* Transmit string */
uart_console_write(port, s, count, mxcuart_console_write_char); //发送数据 /*
* Finally, wait for the transmitter to become empty等待发送完成
*/
do {
status = readl(port->membase + MXC_UARTUSR2);
} while (!(status & MXC_UARTUSR2_TXDC)); /*
* Restore the control registers
*/
writel(oldcr1, port->membase + MXC_UARTUCR1);//恢复串口寄存器数值
writel(oldcr2, port->membase + MXC_UARTUCR2);
writel(oldcr3, port->membase + MXC_UARTUCR3);
}

从这个函数看书,它首先保存了串口控制寄存器的值,然后初始化成符合控制台的,发送完数据后,又恢复了原来的数据

这样做的目的就是,如果我们加载了串口的驱动,那么很有可能打乱了控制台的配置,而系统启动以后

我们还不能动串口驱动的配置,因此最好的办法就是,每次发送数据都重新配置串口,发送完后再恢复以前的配置。

到了这里控制台初始化的第一部分已经完成了。我们可以知道没有文件系统,控制台是怎么工作的。

android kernel控制台初始化过程的更多相关文章

  1. Android 8 Wifi 初始化过程

    记录一下wifi初始化过程. packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java public void on ...

  2. Android编译系统环境初始化过程分析

    Android源代码在编译之前,要先对编译环境进行初始化,其中最主要就是指定编译的类型和目标设备的型号.Android的编译类型主要有eng.userdebug和user三种,而支持的目标设备型号则是 ...

  3. Android(java)学习笔记159:Dalivk虚拟机的初始化过程

    1.初始化下面系统函数(调用dvmStartup函数初始化所有相关的函数) 开始学习虚拟机的初始化过程,先从dvmStartup函数开始,这个函数实现所有开始虚拟机的准备工作: dvmAllocTra ...

  4. Android(java)学习笔记102:Dalivk虚拟机的初始化过程

    1. 初始化下面系统函数(调用dvmStartup函数初始化所有相关的函数) 开始学习虚拟机的初始化过程,先从dvmStartup函数开始,这个函数实现所有开始虚拟机的准备工作:    dvmAllo ...

  5. 【Android 系统开发】下载 编译 Android源代码 和 Android kernel源代码

    下载Android源码简要流程 : a. 获取repo文件: curl http://commondatastorage.googleapis.com/git-repo-downloads/repo ...

  6. [转]在static代码块或static变量的初始化过程中使用ServiceManager提供的api的陷阱

    一. 案例 1.源码: /** @hide */ private TelephonyManager(int slotId) { mContext = null; mSlotId = slotId; i ...

  7. Android 6.0启动过程具体解析

    在之前的一篇文章中.从概念上学习了Andoird系统的启动过程.Android系统启动过程学习 而在这篇文章中,我们将从代码角度细致学习Android系统的启动过程,同一时候,学习Android启动过 ...

  8. 下载 编译 Android源代码 和 Android kernel源代码

    下载Android源码简要流程 : a. 获取repo文件: curl http://commondatastorage.googleapis.com/git-repo-downloads/repo ...

  9. android绘制view的过程

    1 android绘制view的过程简单描述  简单描述可以解释为:计算大小(measure),布局坐标计算(layout),绘制到屏幕(draw):            下面看看每一步的动作到底是 ...

随机推荐

  1. 拆分Cocos2dx 渲染项目 总结

    因为只拆分了渲染的内容,所以代码只针对渲染部分进行分析. 代码涉及到这些类: CCImage,对图片的数据进行操作 CCNode,CCSprite,结点类 CCProgram,CCRenderer,C ...

  2. 安装gitlab管理自己的代码

    安装gitlab的资料网上搜索很多,但发现很多都是比较老的资料了.我把我安装的过程记录一下,应该是最简单的过程了 1. 到 https://about.gitlab.com/downloads/ 下载 ...

  3. 小程序获取openid unionid session_key

    .wxml <button bindtap="paytap">授权</button> .js Page({ paytap: function () { va ...

  4. BZOJ 2049: [Sdoi2008]Cave 洞穴勘测 (动态树入门)

    2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1528  Solved: 644[Submit][ ...

  5. 安装wp8sdk 当前系统时钟或签名文件中的时间戳验证时要求的证书不在有效期内。

    安装wp8sdk 当前系统时钟或签名文件中的时间戳验证时要求的证书不在有效期内. [1404:0090][2015-06-12T08:00:53]: Error 0x800b0101: Failed ...

  6. What is an OPC .NET Wrapper ?

    An OPC .NET wrapper is a software layer that makes OPC COM servers accessible from a .NET client app ...

  7. Android调用手机中的应用市场,去评分的功能实现

    在我们常常使用的软件当中,我们经常可以看到在软件的设置界面,有一个功能那就是去评分的功能,只要我们一点击“去评分”就会调用手机中的应用市场软件.一开始我以为这个功能的实现是要遍历整个手机中的软件包名, ...

  8. 点集转线python最优代码

    import arcpy import os import types def convertPoints(): arcpy.env.overwriteOutput = True inPts = ar ...

  9. SQL Server批量替换全部表中内容sql语句-清楚挂马

    有朋友常常会发现自己的数据库全部的内容给插入了一些代码,假设要一个个表一个个记录去删除.太麻烦了,以下我在在网上找到一个能够批量删除的方法,实际上是批量把那段恶意代码替换,很高速. declare @ ...

  10. ubuntu下如何查看自己的外网IP

    1.1 安装使用curl命令实现      sudo apt-get install curl1.2 输入命令      curl ifconfig.me