rev 0.2
快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
ADI bf561 DSP
uclinux-2008r1.5-rc3 (smp patch)
Visual DSP++ 5.0 (update 5)
欢迎转载,但请保留作者信息
在uclinux初始化过程中,除非启用了early console,否则直到console_init调用之前是没有任何输出的,它们的输出都放在__log_buf这个缓冲内的,在console_init调用时再将这个缓冲区内的数据一次性输出。
那么console_init又做了什么工作呢?这个函数体在drivers\char\tty_io.c中:
/*
* 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..
*/
call = __con_initcall_start;
while (call < __con_initcall_end) {
(*call)();
call++;
}
}
从这个函数大致可以猜出,console和串口的初始化操作应该是由__con_initcall_start到__con_initcall_end之间的函数调用来完成的。那么放到这段空间中的函数是怎么来的呢?又是什么函数可以放在这段空间中呢?
让我们看看vmlinux.lds.s文件中对这两个符号的定义吧:
.con_initcall.init :
{
___con_initcall_start = .;
*(.con_initcall.init)
___con_initcall_end = .;
}
原来,这段空间中存放的是.con_initcall.init这个段的内容,也就是说只要在数据代码中声明.con_initcall.init就可以了。
在uclinux的头文件中搜索initcall,发现了这样一个定义(include\linux\init.h):
#define console_initcall(fn) \
static initcall_t __initcall_##fn \
__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn
在此函数中的所有操作都是与硬件无关的,据此可以猜测,应该还有一个与硬件相关的文件,且在此文件中应该使用console_initcall这个宏。
在内核源码中搜索console_initcall,发现了一大堆结果:
查找全部"console_initcall", 大小写匹配, 全字匹配, 子文件夹, 查找结果1, "D:\uc2008\linux-2.6.x", "*.c"
D:\uc2008\linux-2.6.x\drivers\char\amiserial.c(2132)://console_initcall(amiserial_console_init);
D:\uc2008\linux-2.6.x\drivers\char\decserial.c(66)://console_initcall(decserial_console_init);
D:\uc2008\linux-2.6.x\drivers\char\hvc_beat.c(135)://console_initcall(hvc_beat_console_init);
D:\uc2008\linux-2.6.x\drivers\char\hvc_console.c(242)://console_initcall(hvc_console_init);
D:\uc2008\linux-2.6.x\drivers\char\hvc_iseries.c(594)://console_initcall(hvc_find_vtys);
D:\uc2008\linux-2.6.x\drivers\char\hvc_rtas.c(134)://console_initcall(hvc_rtas_console_init);
D:\uc2008\linux-2.6.x\drivers\char\hvc_vio.c(169)://console_initcall(hvc_find_vtys);
D:\uc2008\linux-2.6.x\drivers\char\hvsi.c(1317)://console_initcall(hvsi_console_init);
D:\uc2008\linux-2.6.x\drivers\char\serial167.c(2602)://console_initcall(serial167_console_init);
D:\uc2008\linux-2.6.x\drivers\char\viocons.c(1167)://console_initcall(viocons_init);
D:\uc2008\linux-2.6.x\drivers\char\vme_scc.c(1044)://console_initcall(vme_scc_console_init);
D:\uc2008\linux-2.6.x\drivers\char\vt.c(2823)://console_initcall(con_init);
D:\uc2008\linux-2.6.x\drivers\s390\char\con3215.c(890)://console_initcall(con3215_init);
D:\uc2008\linux-2.6.x\drivers\s390\char\con3270.c(632)://console_initcall(con3270_init);
D:\uc2008\linux-2.6.x\drivers\s390\char\sclp_con.c(252)://console_initcall(sclp_console_init);
D:\uc2008\linux-2.6.x\drivers\s390\char\sclp_vt220.c(774)://console_initcall(sclp_vt220_con_init);
D:\uc2008\linux-2.6.x\drivers\serial\21285.c(471)://console_initcall(rs285_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\68328serial.c(1577)://console_initcall(m68328_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\8250.c(2539)://console_initcall(serial8250_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\8250_early.c(210)://console_initcall(early_uart_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\atmel_serial.c(863)://console_initcall(atmel_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\bfin_5xx.c(1103)://console_initcall(bfin_serial_rs_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\clps711x.c(534)://console_initcall(clps711xuart_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\dz.c(765)://console_initcall(dz_serial_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\imx.c(1022)://console_initcall(imx_rs_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\m32r_sio.c(1121)://console_initcall(m32r_sio_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\mcfserial.c(1974)://console_initcall(mcfrs_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\mpc52xx_uart.c(800)://console_initcall(mpc52xx_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\netx-serial.c(644)://console_initcall(netx_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\pmac_zilog.c(2024)://console_initcall(pmz_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\pnx8xxx_uart.c(747)://console_initcall(pnx8xxx_rs_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\pxa.c(691)://console_initcall(serial_pxa_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\s3c2410.c(1932)://console_initcall(s3c24xx_serial_initconsole);
D:\uc2008\linux-2.6.x\drivers\serial\sa1100.c(808)://console_initcall(sa1100_rs_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\serial_ks8695.c(611)://console_initcall(ks8695_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\serial_lh7a40x.c(631)://console_initcall (lh7a40xuart_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\serial_txx9.c(922)://console_initcall(serial_txx9_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\sh-sci.c(1280)://console_initcall(sci_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\sh-sci.c(1361)://console_initcall(kgdb_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\sn_console.c(1086)://console_initcall(sn_sal_serial_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\uartlite.c(400)://console_initcall(ulite_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\v850e_uart.c(234)://console_initcall(v850e_uart_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\vr41xx_siu.c(903)://console_initcall(siu_console_init);
D:\uc2008\linux-2.6.x\drivers\serial\cpm_uart\cpm_uart_core.c(1242)://console_initcall(cpm_uart_console_init);
匹配行: 44 匹配文件: 43 合计搜索文件: 6311
果然不出所料,从目录和文件名中可以看出内核为许多不同的设备提供了硬件驱动。因为我用的是BF561,因此使用上述文件中的drivers/serial/bfin_5xx.c文件。
在drivers/serial/bfin_5xx.c中有这样一行:
console_initcall(bfin_serial_rs_console_init);
展开这个宏:
static initcall_t __initcall_bfin_serial_rs_console_init
__attribute_used__ __attribute__((__section__(".con_initcall.init")))= bfin_serial_rs_console_init
上式中initcall_t定义为:
typedef int (*initcall_t)(void);
可以看出它声明了一个函数指针变量,这个变量就放在.con_initcall.init这个数据段中,且这个指针的值指向bfin_serial_rs_console_init函数。这样在console_init函数中就可以通过这个函数指针调用bfin_serial_rs_console_init函数。
bf561串口的初始化就是在bfin_serial_rs_console_init函数中完成的,看看:
static int __init bfin_serial_rs_console_init(void)
{
bfin_serial_init_ports();
register_console(&bfin_serial_console);
return 0;
}
实际上做了两件事情,首先调用bfin_serial_init_ports函数初始化了uart结构体的部分成员,但是它并不初始化硬件,看起来似乎有点奇怪。然后注册一个struct console结构体变量,并将printk缓冲区中的字符输出。
此函数的实现在kernel/printk.c中:
/*
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
* print any messages that were printed by the kernel before the
* console driver was initialized.
*/
void register_console(struct console *console)
{
int i;
unsigned long flags;
struct console *bootconsole = NULL;
if (preferred_console < 0 || bootconsole || !console_drivers)
preferred_console = selected_console;
/*
* See if we want to use this console driver. If we
* didn't select a console we take the first one
* that registers here.
*/
if (preferred_console < 0) {
if (console->index < 0)
console->index = 0;
if (console->setup == NULL ||
console->setup(console, NULL) == 0) {
console->flags |= CON_ENABLED | CON_CONSDEV;
preferred_console = 0;
}
}
/*
* See if this console matches one we selected on
* the command line.
*/
for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
i++) {
if (strcmp(console_cmdline[i].name, console->name) != 0)
continue;
if (console->index >= 0 &&
console->index != console_cmdline[i].index)
continue;
if (console->index < 0)
console->index = console_cmdline[i].index;
if (console->setup &&
console->setup(console, console_cmdline[i].options) != 0)
break;
console->flags |= CON_ENABLED;
console->index = console_cmdline[i].index;
if (i == selected_console) {
console->flags |= CON_CONSDEV;
preferred_console = selected_console;
}
break;
}
if (!(console->flags & CON_ENABLED))
return;
/*
* Put this console in the list - keep the
* preferred driver at the head of the list.
*/
acquire_console_sem();
if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
console->next = console_drivers;
console_drivers = console;
if (console->next)
console->next->flags &= ~CON_CONSDEV;
} else {
console->next = console_drivers->next;
console_drivers->next = console;
}
if (console->flags & CON_PRINTBUFFER) {
/*
* release_console_sem() will print out the buffered messages
* for us.
*/
spin_lock_irqsave(&logbuf_lock, flags);
con_start = log_start;
spin_unlock_irqrestore(&logbuf_lock, flags);
}
release_console_sem();
}
在register_console函数中要进行串口硬件的初始化工作,这个工作是由console结构体中的setup回调函数来完成的:
int (*setup)(struct console *, char *);
在register_console函数中有这样一段代码:
/*
* See if we want to use this console driver. If we
* didn't select a console we take the first one
* that registers here.
*/
if (preferred_console < 0) {
if (console->index < 0)
console->index = 0;
if (console->setup == NULL ||
console->setup(console, NULL) == 0) {
console->flags |= CON_ENABLED | CON_CONSDEV;
preferred_console = 0;
}
}
在此调用了setup回调函数。
在bf561的内核中,此回调函数指向bfin_serial_console_setup,它位于drivers/serial/bfin-5xx.c:
static int __init
bfin_serial_console_setup(struct console *co, char *options)
{
struct bfin_serial_port *uart;
int baud = 57600;
int bits = 8;
int parity = 'n';
int flow = 'n';
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (co->index == -1 || co->index >= nr_ports)
co->index = 0;
uart = &bfin_serial_ports[co->index];
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
bfin_serial_console_get_options(uart, &baud, &parity, &bits);
return uart_set_options(&uart->port, co, baud, parity, bits, flow);
}
在这里,由于在register_console中传递过来的option为NULL,因此将直接调用bfin_serial_console_get_options,而这个函数用于直接从硬件寄存器中读取当前的串口配置,但是它仅适用于boot-loader已经对串口初始化的情况,对于没用boot-loader的情况,它将什么也不做。
uart_set_options的代码如下,它位于drivers/serial/serial_core.c,将进行硬件配置:
/**
* uart_set_options - setup the serial console parameters
* @port: pointer to the serial ports uart_port structure
* @co: console pointer
* @baud: baud rate
* @parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
* @bits: number of data bits
* @flow: flow control character - 'r' (rts)
*/
int __init
uart_set_options(struct uart_port *port, struct console *co,
int baud, int parity, int bits, int flow)
{
struct ktermios termios;
int i;
/*
* Ensure that the serial console lock is initialised
* early.
*/
spin_lock_init(&port->lock);
lockdep_set_class(&port->lock, &port_lock_key);
memset(&termios, 0, sizeof(struct ktermios));
termios.c_cflag = CREAD | HUPCL | CLOCAL;
/*
* Construct a cflag setting.
*/
for (i = 0; baud_rates[i].rate; i++)
if (baud_rates[i].rate <= baud)
break;
termios.c_cflag |= baud_rates[i].cflag;
if (bits == 7)
<span st