zoukankan      html  css  js  c++  java
  • android kernel控制台初始化过程


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

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

    下面正式来看

    板子初始化的过程
    androidkernel_imxarcharmmach-mx6oard-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);
    这个函数就是文件系统挂载之前控制台的初始化函数。下面我就开始分析这个函数
    androidkernel_imxarcharmplat-mxccpu.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);
    androidkernel_imxdrivers	tyserial、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')
    ",
          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);
    }



    从这个函数看书,它首先保存了串口控制寄存器的值,然后初始化成符合控制台的,发送完数据后,又恢复了原来的数据
    这样做的目的就是,如果我们加载了串口的驱动,那么很有可能打乱了控制台的配置,而系统启动以后
    我们还不能动串口驱动的配置,因此最好的办法就是,每次发送数据都重新配置串口,发送完后再恢复以前的配置。
    到了这里控制台初始化的第一部分已经完成了。我们可以知道没有文件系统,控制台是怎么工作的。

  • 相关阅读:
    zencart_magiczoom
    如何把一个TXT文本文件按行数分割成多个文本文件
    工作时常用东西汇总
    asp Vernum
    sitemap制作
    php后门屌炸天
    威盾解密
    软路由
    企业安全工作要点
    内网ARP攻击
  • 原文地址:https://www.cnblogs.com/james1207/p/3253853.html
Copyright © 2011-2022 走看看