int printf(const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer[CONFIG_SYS_PBSIZE];
#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER)
if (!gd->have_console)
return 0;
#endif
va_start(args, fmt);
/* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
/* Print the string */
puts(printbuffer);
return i;
}
这里的va_list、va_start、va_end就不用说了,具体参考http://blog.csdn.net/u013691997/article/details/23426619
#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER)
if (!gd->have_console)
return 0;
#endif
意思是加入没有defineCONFIG_SANDBOX和CONFIG_PRE_CONSOLE_BUFFER
那么则检查gd->have_console,也就是检查是否存在串口,如果不存在串口就要退出,
为什么要说CONFIG_PRE_CONSOLE_BUFFER呢?上面的话反过来就是,如果存在缓冲,
就不用检查是否有串口。再往下看,调用了puts()
void puts(const char *s)
{
#ifdef CONFIG_SANDBOX
if (!gd) {
os_puts(s);
return;
}
#endif
#ifdef CONFIG_SILENT_CONSOLE
if (gd->flags & GD_FLG_SILENT)
return;
#endif
#ifdef CONFIG_DISABLE_CONSOLE
if (gd->flags & GD_FLG_DISABLE_CONSOLE)
return;
#endif
if (!gd->have_console)
return pre_console_puts(s);
if (gd->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fputs(stdout, s);
} else {
/* Send directly to the handler */
serial_puts(s);
}
}
分析可知puts检查have_console、gd->flags & GD_FLG_DEVINIT,也就是
检查是否有串口、如果有是否初始化,注意这里的初始化不同于一般意义上的初始化
搜索项目可知,gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
是在console_init_r中执行的,而SPL阶段根本不会调用console_init_r
所以puts检查是否有串口,没串口就调用pre_console_puts(s);有串口就调用
serial_puts(s);那么分析一下pre_console_puts
#ifdef CONFIG_PRE_CONSOLE_BUFFER
#define CIRC_BUF_IDX(idx) ((idx) % (unsigned long)CONFIG_PRE_CON_BUF_SZ)
static void pre_console_putc(const char c)
{
char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;
buffer[CIRC_BUF_IDX(gd->precon_buf_idx++)] = c;
}
static void pre_console_puts(const char *s)
{
while (*s)
pre_console_putc(*s++);
}
static void print_pre_console_buffer(void)
{
unsigned long i = 0;
char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;
if (gd->precon_buf_idx > CONFIG_PRE_CON_BUF_SZ)
i = gd->precon_buf_idx - CONFIG_PRE_CON_BUF_SZ;
while (i < gd->precon_buf_idx)
putc(buffer[CIRC_BUF_IDX(i++)]);
}
#else
static inline void pre_console_putc(const char c) {}
static inline void pre_console_puts(const char *s) {}
static inline void print_pre_console_buffer(void) {}
#endif
可见,如果定义了缓冲puts将字符串缓冲起来,没有定义缓冲的话什么也不做,
小结一下:
printf执行有三种结果,1.什么都不做只return 0.2.puts字符串到缓冲
3.puts字符串到设备(串口)。
值得注意的是,printf调用puts的话要么输出到缓冲要么输出到设备,而单看puts
时,其有可能什么都不做。如果printf输出到缓冲,然后什么时间处理缓冲呢??
那么回过头来分一下整个preloader_console_init(),只有在get_current()的时候
可能会输出到缓冲(初始化设备失败),而get_current()成功的话,其本身不会
puts错误信息,而之后gd->have_console = 1;所以再调用printf或者puts的话会
直接输出到设备。
/**
* serial_puts() - Output string via currently selected serial port
* @s: Zero-terminated string to be output from the serial port.
*
* This function outputs a zero-terminated string via currently
* selected serial port. This function behaves as an accelerator
* in case the hardware can queue multiple characters for transfer.
* The whole string that is to be output is available to the function
* implementing the hardware manipulation. Transmitting the whole
* string may take some time, thus this function may block for some
* amount of time. This function uses the get_current() call to
* determine which port is selected.
*/
void serial_puts(const char *s)
{
get_current()->puts(s);
}
继续一层一层剥开就是
void
_serial_putc(const char c,const int port)
{
if (c == '\n')
NS16550_putc(PORT, '\r');
NS16550_putc(PORT, c);
}
NS16550的问题就不说了。
一层一层的这么多,这里只想说,printf是一个中间函数,对于它的重定向其实就是给它一个可用的叶子函数,另外每个printf函数的具体过程都不一样,所以在重定向的
方法也不一样,这里跟stm32(keil使用的arm编译器)比较一下就看出来了
再回忆一下a20的uart串口驱动如何与ns16550挂钩的:
preloader_console_init()——>serial_init()——>get_current——>default_serial_console()
preloader_console_init()——>serial_init():由board.c调用spl.c
serial_init()——>get_current:由spl.c调用drivers/serial/serial.c
get_current——>default_serial_console():
default_serial_console()在serial.h中声明,而serial目录下的很多文件都包含这个serial.h
并且实现了default_serial_console()函数,其他目录下也有此函数的实现,所以与
serial_ns16550.c挂钩是通过makefile实现的,另外还要注意为什么攒在serial_ns16550.c
而不是直接ns16550.c
再次编辑添加:貌似再SPL阶段只有printf,而没有scanf。