我们知道,在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
的,不是错误,呵呵。