在VDSP中编译uclinux(4):console实现

 
我们知道,在uclinux初始化过程中,直到console_init调用之前是没有任何输出的,它们的输出都放在 __log_buf这个缓冲内的,在console_init调用时再将这个缓冲区内的数据一次性输出。
那么console_init又做了什么工作呢?
 
/*
 * Initialize the console device. This is called *early*, so
 * we can't necessarily depend on lots of kernel help here.
 * Just do some early initializations, and do the complex setup
 * later.
 */
void __init console_init(void)
{
     initcall_t *call;
 
     /* Setup the default TTY line discipline. */
     (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
 
     /*
      * set up the console device so that later boot sequences can
      * inform about problems etc..
      */
#ifdef CONFIG_EARLY_PRINTK
     disable_early_printk();
#endif
     call = __con_initcall_start;
     while (call < __con_initcall_end) {
         (*call)();
         call++;
     }
}
从这个函数大致可以猜出,console和串口的初始化操作应该是由 __con_initcall_start__con_initcall_end之间的函数调用来完成的。那么放到这段空间中的函数是怎么来的呢?又是什么函数可以放在这段空间中呢?
让我们看看LDF文件中对这两个符号的定义吧:
                     ___con_initcall_start = .;
                     INPUT_SECTIONS($LIBRARIES_SML3_CM(.con_initcall.init))
                     ___con_initcall_end = .;
原来,这段空间中存放的是.con_initcall.init这个段的内容,也就是说只要在数据代码中声明.con_initcall.init就可以了。
在uclinux的头文件中搜索initcall,发现了这样一个定义:
#define console_initcall(fn) /
     static initcall_t __initcall_##fn /
     __attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn
再看看drivers/serial/bfin_5xx.c中有这样一行:
console_initcall(bfin_serial_rs_console_init);
从这里就可以看出它声明了一个函数指针变量,这个变量就放在 .con_initcall.init这个数据段中,且这个指针的值指向 bfin_serial_rs_console_init函数。这样在console_init函数中就可以通过这个函数指针调用 bfin_serial_rs_console_init函数。而对bf561串口的初始化就是在 bfin_serial_rs_console_init函数中完成的,这个函数同时还将缓冲区内的数据从串口输出。
在移植这部分代码的时候碰到了一个问题,在bfin_5xx.c中有一个函数 bfin_serial_set_termios 它的作用是实际设置串口参数,但是在设置参数之前它要求发送缓冲区为空:
     do {
         lsr = UART_GET_LSR(uart);
     } while (!(lsr & TEMT));
即它要示UART_LSR寄存器中的TEMT这个位为1,但是实际运行到这里的时候,这一位为0,因此进入死循环。
跟踪发现,在head.s中有一段设置UART的代码:
 
       /* Initialise UART*/
       p0.h = hi(UART_LCR);
       p0.l = lo(UART_LCR);
       r0 = 0x80(Z); /* 未修改前为 0x0(Z)*/
       w[p0] = r0.L; /* To enable DLL writes */
       ssync;
 
       p0.h = hi(UART_DLL);
       p0.l = lo(UART_DLL);
       r0 = 0x0(Z);
       w[p0] = r0.L;
       ssync;
 
       p0.h = hi(UART_DLH);
       p0.l = lo(UART_DLH);
       r0 = 0x00(Z);
       w[p0] = r0.L;
       ssync;
 
       p0.h = hi(UART_GCTL);
       p0.l = lo(UART_GCTL);
       r0 = 0x0(Z);
       w[p0] = r0.L; /* To enable UART clock */
       ssync;
我们知道在设置UART_DLL和UART_DLH时必须将UART_LCR的最高位设置为1,但是原始代码中却未设置,这样就造成了设置UART_DLL的失败,此时UART_LCR的TEMT标志将被设置为0,从而导致了死循环问题的发生。将上述红色标志的代码修改后解决此问题。
不过奇怪的是,uclinux在gcc下编译居然不会有这样的问题发生!!为什么??
程序运行到这里,就已经可以通过串口看到先前输出缓冲区中的数据了,先自我庆祝一下!哈哈!
Linux version 2.6.19.3-ADI-2007R1.1-svn ([email protected]) (vdsp 4.5) #2 Tue Sep 25 1
1:24:43 CST 2007
Blackfin support (C) 2004-2007 Analog Devices, Inc.
Compiled for ADSP-BF561 Rev 0.3
Blackfin Linux support by http://blackfin.uclinux.org/
Processor Speed: 594 MHz core clock and 99 Mhz System Clock
Board Memory: 4MB
Kernel Managed Memory: 4MB
Memory map:
 text       = 0x00010000-0x00020544
 init       = 0x00020544-0x0002d014
 data       = 0x0002fa08-0x00032cc4
 stack      = 0x00030000-0x00032000
 bss        = 0x00032cc4-0x00032cc4
 available = 0x00032cc4-0x003ff000
Data Cache Enabled
Hardware Trace Enabled
Built 1 zonelists. Total pages: 1016
Kernel command line:
注意 SDRAM 是有意改为 4M 的,不是错误,呵呵。 

你可能感兴趣的:(在VDSP中编译uclinux(4):console实现)